mirror of
https://github.com/TheNetworkGuy/netbox-zabbix-sync.git
synced 2025-07-14 01:41:25 -06:00
🏷️ Changed all occurences of "Netbox" to "NetBox" as per the [NetBox Style Guide](https://netboxlabs.com/docs/netbox/en/stable/development/style-guide/).
This commit is contained in:
parent
434722df53
commit
d0941ff909
46
README.md
46
README.md
@ -1,6 +1,6 @@
|
|||||||
# Netbox to Zabbix synchronization
|
# NetBox to Zabbix synchronization
|
||||||
|
|
||||||
A script to create, update and delete Zabbix hosts using Netbox device objects.
|
A script to create, update and delete Zabbix hosts using NetBox device objects.
|
||||||
|
|
||||||
## Installation via Docker
|
## Installation via Docker
|
||||||
|
|
||||||
@ -81,16 +81,16 @@ password. In that case `ZABBIX_USER` and `ZABBIX_PASS` will be ignored.
|
|||||||
ZABBIX_TOKEN=othersecrettoken
|
ZABBIX_TOKEN=othersecrettoken
|
||||||
```
|
```
|
||||||
|
|
||||||
If you are using custom SSL certificates for Netbox and/or Zabbix, you can set
|
If you are using custom SSL certificates for NetBox and/or Zabbix, you can set
|
||||||
the following environment variable to the path of your CA bundle file:
|
the following environment variable to the path of your CA bundle file:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
REQUEST_CA_BUNDLE=/path/to/your/ca-bundle.crt
|
REQUEST_CA_BUNDLE=/path/to/your/ca-bundle.crt
|
||||||
```
|
```
|
||||||
|
|
||||||
### Netbox custom fields
|
### NetBox custom fields
|
||||||
|
|
||||||
Use the following custom fields in Netbox (if you are using config context for
|
Use the following custom fields in NetBox (if you are using config context for
|
||||||
the template information then the zabbix_template field is not required):
|
the template information then the zabbix_template field is not required):
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -118,7 +118,7 @@ manually change the ID. For example to re-run a sync.
|
|||||||
## Virtual Machine (VM) Syncing
|
## Virtual Machine (VM) Syncing
|
||||||
|
|
||||||
In order to use VM syncing, make sure that the `zabbix_id` custom field is also
|
In order to use VM syncing, make sure that the `zabbix_id` custom field is also
|
||||||
present on Virtual machine objects in Netbox.
|
present on Virtual machine objects in NetBox.
|
||||||
|
|
||||||
Use the `config.py` file and set the `sync_vms` variable to `True`.
|
Use the `config.py` file and set the `sync_vms` variable to `True`.
|
||||||
|
|
||||||
@ -128,7 +128,7 @@ hostgroups. The default is `cluster_type/cluster/role`.
|
|||||||
To enable filtering for VM's, check the `nb_vm_filter` variable out. It works
|
To enable filtering for VM's, check the `nb_vm_filter` variable out. It works
|
||||||
the same as with the device filter (see documentation under "Hostgroup layout").
|
the same as with the device filter (see documentation under "Hostgroup layout").
|
||||||
Note that not all filtering capabilities and properties of devices are
|
Note that not all filtering capabilities and properties of devices are
|
||||||
applicable to VM's and vice-versa. Check the Netbox API documentation to see
|
applicable to VM's and vice-versa. Check the NetBox API documentation to see
|
||||||
which filtering options are available for each object type.
|
which filtering options are available for each object type.
|
||||||
|
|
||||||
## Config file
|
## Config file
|
||||||
@ -260,15 +260,15 @@ in an error:
|
|||||||
```
|
```
|
||||||
hostgroup_format = "mycustomfieldname"
|
hostgroup_format = "mycustomfieldname"
|
||||||
|
|
||||||
Netbox-Zabbix-sync - ERROR - ESXI1 has no reliable hostgroup. This is most likely due to the use of custom fields that are empty.
|
NetBox-Zabbix-sync - ERROR - ESXI1 has no reliable hostgroup. This is most likely due to the use of custom fields that are empty.
|
||||||
```
|
```
|
||||||
|
|
||||||
### Device status
|
### Device status
|
||||||
|
|
||||||
By setting a status on a Netbox device you determine how the host is added (or
|
By setting a status on a NetBox device you determine how the host is added (or
|
||||||
updated) in Zabbix. There are, by default, 3 options:
|
updated) in Zabbix. There are, by default, 3 options:
|
||||||
|
|
||||||
- Delete the host from Zabbix (triggered by Netbox status "Decommissioning" and
|
- Delete the host from Zabbix (triggered by NetBox status "Decommissioning" and
|
||||||
"Inventory")
|
"Inventory")
|
||||||
- Create the host in Zabbix but with a disabled status (Trigger by "Offline",
|
- Create the host in Zabbix but with a disabled status (Trigger by "Offline",
|
||||||
"Planned", "Staged" and "Failed")
|
"Planned", "Staged" and "Failed")
|
||||||
@ -284,8 +284,8 @@ script:
|
|||||||
### Zabbix Inventory
|
### Zabbix Inventory
|
||||||
|
|
||||||
This script allows you to enable the inventory on managed Zabbix hosts and sync
|
This script allows you to enable the inventory on managed Zabbix hosts and sync
|
||||||
NetBox device properties to the specified inventory fields. To map Netbox
|
NetBox device properties to the specified inventory fields. To map NetBox
|
||||||
information to Netbox inventory fields, set `inventory_sync` to `True`.
|
information to NetBox inventory fields, set `inventory_sync` to `True`.
|
||||||
|
|
||||||
You can set the inventory mode to "disabled", "manual" or "automatic" with the
|
You can set the inventory mode to "disabled", "manual" or "automatic" with the
|
||||||
`inventory_mode` variable. See
|
`inventory_mode` variable. See
|
||||||
@ -310,7 +310,7 @@ fields.
|
|||||||
|
|
||||||
### Template source
|
### Template source
|
||||||
|
|
||||||
You can either use a Netbox device type custom field or Netbox config context
|
You can either use a NetBox device type custom field or NetBox config context
|
||||||
for the Zabbix template information.
|
for the Zabbix template information.
|
||||||
|
|
||||||
Using a custom field allows for only one template. You can assign multiple
|
Using a custom field allows for only one template. You can assign multiple
|
||||||
@ -342,7 +342,7 @@ in the config context in this format:
|
|||||||
```
|
```
|
||||||
|
|
||||||
You can also opt for the default device type custom field behaviour but with the
|
You can also opt for the default device type custom field behaviour but with the
|
||||||
added benefit of overwriting the template should a device in Netbox have a
|
added benefit of overwriting the template should a device in NetBox have a
|
||||||
device specific context defined. In this case the device specific context
|
device specific context defined. In this case the device specific context
|
||||||
template(s) will take priority over the device type custom field template.
|
template(s) will take priority over the device type custom field template.
|
||||||
|
|
||||||
@ -352,9 +352,9 @@ templates_config_context_overrule = True
|
|||||||
|
|
||||||
## Permissions
|
## Permissions
|
||||||
|
|
||||||
### Netbox
|
### NetBox
|
||||||
|
|
||||||
Make sure that the Netbox user has proper permissions for device read and modify
|
Make sure that the NetBox user has proper permissions for device read and modify
|
||||||
(modify to set the Zabbix HostID custom field) operations. The user should also
|
(modify to set the Zabbix HostID custom field) operations. The user should also
|
||||||
have read-only access to the device types.
|
have read-only access to the device types.
|
||||||
|
|
||||||
@ -435,13 +435,13 @@ In the example above the host will use the group on Zabbix 7. On Zabbix 6 and
|
|||||||
below the host will use the proxy. Zabbix 7 will use the proxy value when
|
below the host will use the proxy. Zabbix 7 will use the proxy value when
|
||||||
ommiting the proxy_group value.
|
ommiting the proxy_group value.
|
||||||
|
|
||||||
Because of the possible amount of destruction when setting up Netbox but
|
Because of the possible amount of destruction when setting up NetBox but
|
||||||
forgetting the proxy command, the sync works a bit different. By default
|
forgetting the proxy command, the sync works a bit different. By default
|
||||||
everything is synced except in a situation where the Zabbix host has a proxy
|
everything is synced except in a situation where the Zabbix host has a proxy
|
||||||
configured but nothing is configured in Netbox. To force deletion and a full
|
configured but nothing is configured in NetBox. To force deletion and a full
|
||||||
sync, set the `full_proxy_sync` variable in the config file.
|
sync, set the `full_proxy_sync` variable in the config file.
|
||||||
|
|
||||||
### Set interface parameters within Netbox
|
### Set interface parameters within NetBox
|
||||||
|
|
||||||
When adding a new device, you can set the interface type with custom context. By
|
When adding a new device, you can set the interface type with custom context. By
|
||||||
default, the following configuration is applied when no config context is
|
default, the following configuration is applied when no config context is
|
||||||
@ -453,14 +453,14 @@ provided:
|
|||||||
- SNMP community: {$SNMP_COMMUNITY}
|
- SNMP community: {$SNMP_COMMUNITY}
|
||||||
|
|
||||||
Due to Zabbix limitations of changing interface type with a linked template,
|
Due to Zabbix limitations of changing interface type with a linked template,
|
||||||
changing the interface type from within Netbox is not supported and the script
|
changing the interface type from within NetBox is not supported and the script
|
||||||
will generate an error.
|
will generate an error.
|
||||||
|
|
||||||
For example when changing a SNMP interface to an Agent interface:
|
For example when changing a SNMP interface to an Agent interface:
|
||||||
|
|
||||||
```
|
```
|
||||||
Netbox-Zabbix-sync - WARNING - Device: Interface OUT of sync.
|
NetBox-Zabbix-sync - WARNING - Device: Interface OUT of sync.
|
||||||
Netbox-Zabbix-sync - ERROR - Device: changing interface type to 1 is not supported.
|
NetBox-Zabbix-sync - ERROR - Device: changing interface type to 1 is not supported.
|
||||||
```
|
```
|
||||||
|
|
||||||
To configure the interface parameters you'll need to use custom context. Custom
|
To configure the interface parameters you'll need to use custom context. Custom
|
||||||
@ -519,7 +519,7 @@ environment. For example, you could:
|
|||||||
```
|
```
|
||||||
|
|
||||||
I would recommend using macros for sensitive data such as community strings
|
I would recommend using macros for sensitive data such as community strings
|
||||||
since the data in Netbox is plain-text.
|
since the data in NetBox is plain-text.
|
||||||
|
|
||||||
> **_NOTE:_** Not all SNMP data is required for a working configuration.
|
> **_NOTE:_** Not all SNMP data is required for a working configuration.
|
||||||
> [The following parameters are allowed](https://www.zabbix.com/documentation/current/manual/api/reference/hostinterface/object#details_tag "The following parameters are allowed")but
|
> [The following parameters are allowed](https://www.zabbix.com/documentation/current/manual/api/reference/hostinterface/object#details_tag "The following parameters are allowed")but
|
||||||
|
@ -7,7 +7,7 @@ templates_config_context = False
|
|||||||
# higher priority then custom field templates
|
# higher priority then custom field templates
|
||||||
templates_config_context_overrule = False
|
templates_config_context_overrule = False
|
||||||
|
|
||||||
# Set template and device Netbox "custom field" names
|
# Set template and device NetBox "custom field" names
|
||||||
# Template_cf is not used when templates_config_context is enabled
|
# Template_cf is not used when templates_config_context is enabled
|
||||||
template_cf = "zabbix_template"
|
template_cf = "zabbix_template"
|
||||||
device_cf = "zabbix_hostid"
|
device_cf = "zabbix_hostid"
|
||||||
@ -35,7 +35,7 @@ vm_hostgroup_format = "cluster_type/cluster/role"
|
|||||||
# With this option disabled proxy's will only be added and modified for Zabbix hosts.
|
# With this option disabled proxy's will only be added and modified for Zabbix hosts.
|
||||||
full_proxy_sync = False
|
full_proxy_sync = False
|
||||||
|
|
||||||
## Netbox to Zabbix device state convertion
|
## NetBox to Zabbix device state convertion
|
||||||
zabbix_device_removal = ["Decommissioning", "Inventory"]
|
zabbix_device_removal = ["Decommissioning", "Inventory"]
|
||||||
zabbix_device_disable = ["Offline", "Planned", "Staged", "Failed"]
|
zabbix_device_disable = ["Offline", "Planned", "Staged", "Failed"]
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ traverse_site_groups = False
|
|||||||
# nb_device_filter = {"site": ["HQ-AMS", "HQ-FRA"]} #Device must be in either one of these sites
|
# nb_device_filter = {"site": ["HQ-AMS", "HQ-FRA"]} #Device must be in either one of these sites
|
||||||
# nb_device_filter = {"site": "HQ-AMS", "tag": "zabbix", "role__n": ["PDU", "console-server"]} #Device must be in site HQ-AMS, have the tag zabbix and must not be part of the PDU or console-server role
|
# nb_device_filter = {"site": "HQ-AMS", "tag": "zabbix", "role__n": ["PDU", "console-server"]} #Device must be in site HQ-AMS, have the tag zabbix and must not be part of the PDU or console-server role
|
||||||
|
|
||||||
# Default device filter, only get devices which have a name in Netbox:
|
# Default device filter, only get devices which have a name in NetBox:
|
||||||
nb_device_filter = {"name__n": "null"}
|
nb_device_filter = {"name__n": "null"}
|
||||||
# Default filter for VMs
|
# Default filter for VMs
|
||||||
nb_vm_filter = {"name__n": "null"}
|
nb_vm_filter = {"name__n": "null"}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# pylint: disable=invalid-name, logging-not-lazy, too-many-locals, logging-fstring-interpolation, too-many-lines
|
# pylint: disable=invalid-name, logging-not-lazy, too-many-locals, logging-fstring-interpolation, too-many-lines
|
||||||
"""
|
"""
|
||||||
Device specific handeling for Netbox to Zabbix
|
Device specific handeling for NetBox to Zabbix
|
||||||
"""
|
"""
|
||||||
from os import sys
|
from os import sys
|
||||||
from re import search
|
from re import search
|
||||||
@ -29,7 +29,7 @@ class PhysicalDevice():
|
|||||||
# pylint: disable=too-many-instance-attributes, too-many-arguments, too-many-positional-arguments
|
# pylint: disable=too-many-instance-attributes, too-many-arguments, too-many-positional-arguments
|
||||||
"""
|
"""
|
||||||
Represents Network device.
|
Represents Network device.
|
||||||
INPUT: (Netbox device class, ZabbixAPI class, journal flag, NB journal class)
|
INPUT: (NetBox device class, ZabbixAPI class, journal flag, NB journal class)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, nb, zabbix, nb_journal_class, nb_version, journal=None, logger=None):
|
def __init__(self, nb, zabbix, nb_journal_class, nb_version, journal=None, logger=None):
|
||||||
@ -92,7 +92,7 @@ class PhysicalDevice():
|
|||||||
self.visible_name = self.nb.name
|
self.visible_name = self.nb.name
|
||||||
self.use_visible_name = True
|
self.use_visible_name = True
|
||||||
self.logger.info(f"Host {self.visible_name} contains special characters. "
|
self.logger.info(f"Host {self.visible_name} contains special characters. "
|
||||||
f"Using {self.name} as name for the Netbox object "
|
f"Using {self.name} as name for the NetBox object "
|
||||||
f"and using {self.visible_name} as visible name in Zabbix.")
|
f"and using {self.visible_name} as visible name in Zabbix.")
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
@ -164,7 +164,7 @@ class PhysicalDevice():
|
|||||||
# Set inventory mode. Default is disabled (see class init function).
|
# Set inventory mode. Default is disabled (see class init function).
|
||||||
if inventory_mode == "disabled":
|
if inventory_mode == "disabled":
|
||||||
if inventory_sync:
|
if inventory_sync:
|
||||||
self.logger.error(f"Host {self.name}: Unable to map Netbox inventory to Zabbix. "
|
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.")
|
"Inventory sync is enabled in config but inventory mode is disabled.")
|
||||||
return True
|
return True
|
||||||
if inventory_mode == "manual":
|
if inventory_mode == "manual":
|
||||||
@ -194,7 +194,7 @@ class PhysicalDevice():
|
|||||||
self.inventory[zbx_inv_field] = str(value)
|
self.inventory[zbx_inv_field] = str(value)
|
||||||
elif not value:
|
elif not value:
|
||||||
# empty value should just be an empty string for API compatibility
|
# empty value should just be an empty string for API compatibility
|
||||||
self.logger.debug(f"Host {self.name}: Netbox inventory lookup for "
|
self.logger.debug(f"Host {self.name}: NetBox inventory lookup for "
|
||||||
f"'{nb_inv_field}' returned an empty value")
|
f"'{nb_inv_field}' returned an empty value")
|
||||||
self.inventory[zbx_inv_field] = ""
|
self.inventory[zbx_inv_field] = ""
|
||||||
else:
|
else:
|
||||||
@ -221,7 +221,7 @@ class PhysicalDevice():
|
|||||||
self.logger.warning(e)
|
self.logger.warning(e)
|
||||||
raise SyncInventoryError(e)
|
raise SyncInventoryError(e)
|
||||||
if not self.nb.virtual_chassis.master:
|
if not self.nb.virtual_chassis.master:
|
||||||
e = (f"{self.name} is part of a Netbox virtual chassis which does "
|
e = (f"{self.name} is part of a NetBox virtual chassis which does "
|
||||||
"not have a master configured. Skipping for this reason.")
|
"not have a master configured. Skipping for this reason.")
|
||||||
self.logger.error(e)
|
self.logger.error(e)
|
||||||
raise SyncInventoryError(e)
|
raise SyncInventoryError(e)
|
||||||
@ -256,7 +256,7 @@ class PhysicalDevice():
|
|||||||
raise SyncInventoryError()
|
raise SyncInventoryError()
|
||||||
# Set variable to empty list
|
# Set variable to empty list
|
||||||
self.zbx_templates = []
|
self.zbx_templates = []
|
||||||
# Go through all templates definded in Netbox
|
# Go through all templates definded in NetBox
|
||||||
for nb_template in self.zbx_template_names:
|
for nb_template in self.zbx_template_names:
|
||||||
template_match = False
|
template_match = False
|
||||||
# Go through all templates found in Zabbix
|
# Go through all templates found in Zabbix
|
||||||
@ -295,7 +295,7 @@ class PhysicalDevice():
|
|||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
"""
|
"""
|
||||||
Removes device from external resources.
|
Removes device from external resources.
|
||||||
Resets custom fields in Netbox.
|
Resets custom fields in NetBox.
|
||||||
"""
|
"""
|
||||||
if self.zabbix_id:
|
if self.zabbix_id:
|
||||||
try:
|
try:
|
||||||
@ -303,7 +303,7 @@ class PhysicalDevice():
|
|||||||
zbx_host = bool(self.zabbix.host.get(filter={'hostid': self.zabbix_id},
|
zbx_host = bool(self.zabbix.host.get(filter={'hostid': self.zabbix_id},
|
||||||
output=[]))
|
output=[]))
|
||||||
e = (f"Host {self.name}: was already deleted from Zabbix."
|
e = (f"Host {self.name}: was already deleted from Zabbix."
|
||||||
" Removed link in Netbox.")
|
" Removed link in NetBox.")
|
||||||
if zbx_host:
|
if zbx_host:
|
||||||
# Delete host should it exists
|
# Delete host should it exists
|
||||||
self.zabbix.host.delete(self.zabbix_id)
|
self.zabbix.host.delete(self.zabbix_id)
|
||||||
@ -317,7 +317,7 @@ class PhysicalDevice():
|
|||||||
raise SyncExternalError(message) from e
|
raise SyncExternalError(message) from e
|
||||||
|
|
||||||
def _zeroize_cf(self):
|
def _zeroize_cf(self):
|
||||||
"""Sets the hostID custom field in Netbox to zero,
|
"""Sets the hostID custom field in NetBox to zero,
|
||||||
effectively destroying the link"""
|
effectively destroying the link"""
|
||||||
self.nb.custom_fields[device_cf] = None
|
self.nb.custom_fields[device_cf] = None
|
||||||
self.nb.save()
|
self.nb.save()
|
||||||
@ -336,13 +336,13 @@ class PhysicalDevice():
|
|||||||
|
|
||||||
def setInterfaceDetails(self):
|
def setInterfaceDetails(self):
|
||||||
"""
|
"""
|
||||||
Checks interface parameters from Netbox and
|
Checks interface parameters from NetBox and
|
||||||
creates a model for the interface to be used in Zabbix.
|
creates a model for the interface to be used in Zabbix.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Initiate interface class
|
# Initiate interface class
|
||||||
interface = ZabbixInterface(self.nb.config_context, self.ip)
|
interface = ZabbixInterface(self.nb.config_context, self.ip)
|
||||||
# Check if Netbox has device context.
|
# Check if NetBox has device context.
|
||||||
# If not fall back to old config.
|
# If not fall back to old config.
|
||||||
if interface.get_context():
|
if interface.get_context():
|
||||||
# If device is SNMP type, add aditional information.
|
# If device is SNMP type, add aditional information.
|
||||||
@ -377,7 +377,7 @@ class PhysicalDevice():
|
|||||||
# Only insert groups in front of list for Zabbix7
|
# Only insert groups in front of list for Zabbix7
|
||||||
proxy_types.insert(0, "proxy_group")
|
proxy_types.insert(0, "proxy_group")
|
||||||
for proxy_type in proxy_types:
|
for proxy_type in proxy_types:
|
||||||
# Check if the key exists in Netbox CC
|
# Check if the key exists in NetBox CC
|
||||||
if proxy_type in self.nb.config_context["zabbix"]:
|
if proxy_type in self.nb.config_context["zabbix"]:
|
||||||
proxy_name = self.nb.config_context["zabbix"][proxy_type]
|
proxy_name = self.nb.config_context["zabbix"][proxy_type]
|
||||||
# go through all proxies
|
# go through all proxies
|
||||||
@ -395,9 +395,9 @@ class PhysicalDevice():
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def createInZabbix(self, groups, templates, proxies,
|
def createInZabbix(self, groups, templates, proxies,
|
||||||
description="Host added by Netbox sync script."):
|
description="Host added by NetBox sync script."):
|
||||||
"""
|
"""
|
||||||
Creates Zabbix host object with parameters from Netbox object.
|
Creates Zabbix host object with parameters from NetBox object.
|
||||||
"""
|
"""
|
||||||
# Check if hostname is already present in Zabbix
|
# Check if hostname is already present in Zabbix
|
||||||
if not self._zabbixHostnameExists():
|
if not self._zabbixHostnameExists():
|
||||||
@ -445,7 +445,7 @@ class PhysicalDevice():
|
|||||||
e = f"Host {self.name}: Couldn't create. Zabbix returned {str(e)}."
|
e = f"Host {self.name}: Couldn't create. Zabbix returned {str(e)}."
|
||||||
self.logger.error(e)
|
self.logger.error(e)
|
||||||
raise SyncExternalError(e) from None
|
raise SyncExternalError(e) from None
|
||||||
# Set Netbox custom field to hostID value.
|
# Set NetBox custom field to hostID value.
|
||||||
self.nb.custom_fields[device_cf] = int(self.zabbix_id)
|
self.nb.custom_fields[device_cf] = int(self.zabbix_id)
|
||||||
self.nb.save()
|
self.nb.save()
|
||||||
msg = f"Host {self.name}: Created host in Zabbix."
|
msg = f"Host {self.name}: Created host in Zabbix."
|
||||||
@ -511,7 +511,7 @@ class PhysicalDevice():
|
|||||||
def ConsistencyCheck(self, groups, templates, proxies, proxy_power, create_hostgroups):
|
def ConsistencyCheck(self, groups, templates, proxies, proxy_power, create_hostgroups):
|
||||||
# pylint: disable=too-many-branches, too-many-statements
|
# pylint: disable=too-many-branches, too-many-statements
|
||||||
"""
|
"""
|
||||||
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.hostgroup.split('/')) > 1:
|
||||||
@ -548,7 +548,7 @@ class PhysicalDevice():
|
|||||||
if len(host) == 0:
|
if len(host) == 0:
|
||||||
e = (f"Host {self.name}: No Zabbix host found. "
|
e = (f"Host {self.name}: No Zabbix host found. "
|
||||||
f"This is likely the result of a deleted Zabbix host "
|
f"This is likely the result of a deleted Zabbix host "
|
||||||
f"without zeroing the ID field in Netbox.")
|
f"without zeroing the ID field in NetBox.")
|
||||||
self.logger.error(e)
|
self.logger.error(e)
|
||||||
raise SyncInventoryError(e)
|
raise SyncInventoryError(e)
|
||||||
host = host[0]
|
host = host[0]
|
||||||
@ -615,7 +615,7 @@ class PhysicalDevice():
|
|||||||
"monitored_by": self.zbxproxy['monitored_by']}
|
"monitored_by": self.zbxproxy['monitored_by']}
|
||||||
self.updateZabbixHost(**update_data)
|
self.updateZabbixHost(**update_data)
|
||||||
else:
|
else:
|
||||||
# No proxy is defined in Netbox
|
# No proxy is defined in NetBox
|
||||||
proxy_set = False
|
proxy_set = False
|
||||||
# Check if a proxy is defined. Uses the proxy_hostid key for backwards compatibility
|
# Check if a proxy is defined. Uses the proxy_hostid key for backwards compatibility
|
||||||
for key in ("proxy_hostid", "proxyid", "proxy_groupid"):
|
for key in ("proxy_hostid", "proxyid", "proxy_groupid"):
|
||||||
@ -624,7 +624,7 @@ class PhysicalDevice():
|
|||||||
proxy_set = True
|
proxy_set = True
|
||||||
if proxy_power and proxy_set:
|
if proxy_power and proxy_set:
|
||||||
# Zabbix <= 6 fix
|
# Zabbix <= 6 fix
|
||||||
self.logger.warning(f"Host {self.name}: no proxy is configured in Netbox "
|
self.logger.warning(f"Host {self.name}: no proxy is configured in NetBox "
|
||||||
"but is configured in Zabbix. Removing proxy config in Zabbix")
|
"but is configured in Zabbix. Removing proxy config in Zabbix")
|
||||||
if "proxy_hostid" in host and bool(host["proxy_hostid"]):
|
if "proxy_hostid" in host and bool(host["proxy_hostid"]):
|
||||||
self.updateZabbixHost(proxy_hostid=0)
|
self.updateZabbixHost(proxy_hostid=0)
|
||||||
@ -638,7 +638,7 @@ class PhysicalDevice():
|
|||||||
if proxy_set and not proxy_power:
|
if proxy_set and not proxy_power:
|
||||||
# Display error message
|
# Display error message
|
||||||
self.logger.error(f"Host {self.name} is configured "
|
self.logger.error(f"Host {self.name} is configured "
|
||||||
f"with proxy in Zabbix but not in Netbox. The"
|
f"with proxy in Zabbix but not in NetBox. The"
|
||||||
" -p flag was ommited: no "
|
" -p flag was ommited: no "
|
||||||
"changes have been made.")
|
"changes have been made.")
|
||||||
if not proxy_set:
|
if not proxy_set:
|
||||||
@ -663,7 +663,7 @@ class PhysicalDevice():
|
|||||||
updates = {}
|
updates = {}
|
||||||
# Go through each key / item and check if it matches Zabbix
|
# Go through each key / item and check if it matches Zabbix
|
||||||
for key, item in self.setInterfaceDetails()[0].items():
|
for key, item in self.setInterfaceDetails()[0].items():
|
||||||
# Check if Netbox value is found in Zabbix
|
# Check if NetBox value is found in Zabbix
|
||||||
if key in host["interfaces"][0]:
|
if key in host["interfaces"][0]:
|
||||||
# If SNMP is used, go through nested dict
|
# If SNMP is used, go through nested dict
|
||||||
# to compare SNMP parameters
|
# to compare SNMP parameters
|
||||||
@ -724,8 +724,8 @@ class PhysicalDevice():
|
|||||||
|
|
||||||
def create_journal_entry(self, severity, message):
|
def create_journal_entry(self, severity, message):
|
||||||
"""
|
"""
|
||||||
Send a new Journal entry to Netbox. Usefull for viewing actions
|
Send a new Journal entry to NetBox. Usefull for viewing actions
|
||||||
in Netbox without having to look in Zabbix or the script log output
|
in NetBox without having to look in Zabbix or the script log output
|
||||||
"""
|
"""
|
||||||
if self.journal:
|
if self.journal:
|
||||||
# Check if the severity is valid
|
# Check if the severity is valid
|
||||||
@ -739,7 +739,7 @@ class PhysicalDevice():
|
|||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
self.nb_journals.create(journal)
|
self.nb_journals.create(journal)
|
||||||
self.logger.debug(f"Host {self.name}: Created journal entry in Netbox")
|
self.logger.debug(f"Host {self.name}: Created journal entry in NetBox")
|
||||||
return True
|
return True
|
||||||
except JournalError(e) as e:
|
except JournalError(e) as e:
|
||||||
self.logger.warning("Unable to create journal entry for "
|
self.logger.warning("Unable to create journal entry for "
|
||||||
@ -749,14 +749,14 @@ class PhysicalDevice():
|
|||||||
|
|
||||||
def zbx_template_comparer(self, tmpls_from_zabbix):
|
def zbx_template_comparer(self, tmpls_from_zabbix):
|
||||||
"""
|
"""
|
||||||
Compares the Netbox and Zabbix templates with each other.
|
Compares the NetBox and Zabbix templates with each other.
|
||||||
Should there be a mismatch then the function will return false
|
Should there be a mismatch then the function will return false
|
||||||
|
|
||||||
INPUT: list of NB and ZBX templates
|
INPUT: list of NB and ZBX templates
|
||||||
OUTPUT: Boolean True/False
|
OUTPUT: Boolean True/False
|
||||||
"""
|
"""
|
||||||
succesfull_templates = []
|
succesfull_templates = []
|
||||||
# Go through each Netbox template
|
# Go through each NetBox template
|
||||||
for nb_tmpl in self.zbx_templates:
|
for nb_tmpl in self.zbx_templates:
|
||||||
# Go through each Zabbix template
|
# Go through each Zabbix template
|
||||||
for pos, zbx_tmpl in enumerate(tmpls_from_zabbix):
|
for pos, zbx_tmpl in enumerate(tmpls_from_zabbix):
|
||||||
@ -770,7 +770,7 @@ class PhysicalDevice():
|
|||||||
f"{nb_tmpl['name']} is present in Zabbix.")
|
f"{nb_tmpl['name']} is present in Zabbix.")
|
||||||
break
|
break
|
||||||
if len(succesfull_templates) == len(self.zbx_templates) and len(tmpls_from_zabbix) == 0:
|
if len(succesfull_templates) == len(self.zbx_templates) and len(tmpls_from_zabbix) == 0:
|
||||||
# All of the Netbox templates have been confirmed as successfull
|
# All of the NetBox templates have been confirmed as successfull
|
||||||
# and the ZBX template list is empty. This means that
|
# and the ZBX template list is empty. This means that
|
||||||
# all of the templates match.
|
# all of the templates match.
|
||||||
return True
|
return True
|
||||||
|
@ -34,7 +34,7 @@ class Hostgroup():
|
|||||||
format_options = {}
|
format_options = {}
|
||||||
# Set variables for both type of devices
|
# Set variables for both type of devices
|
||||||
if self.type in ("vm", "dev"):
|
if self.type in ("vm", "dev"):
|
||||||
# Role fix for Netbox <=3
|
# Role fix for NetBox <=3
|
||||||
role = None
|
role = None
|
||||||
if self.nb_version.startswith(("2", "3")) and self.type == "dev":
|
if self.nb_version.startswith(("2", "3")) and self.type == "dev":
|
||||||
role = self.nb.device_role.name if self.nb.device_role else None
|
role = self.nb.device_role.name if self.nb.device_role else None
|
||||||
@ -129,7 +129,7 @@ class Hostgroup():
|
|||||||
|
|
||||||
def custom_field_lookup(self, hg_category):
|
def custom_field_lookup(self, hg_category):
|
||||||
"""
|
"""
|
||||||
Checks if a valid custom field is present in Netbox.
|
Checks if a valid custom field is present in NetBox.
|
||||||
INPUT: Custom field name
|
INPUT: Custom field name
|
||||||
OUTPUT: dictionary with 'result' and 'cf' keys.
|
OUTPUT: dictionary with 'result' and 'cf' keys.
|
||||||
"""
|
"""
|
||||||
|
@ -29,7 +29,7 @@ class ZabbixInterface():
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def get_context(self):
|
def get_context(self):
|
||||||
""" check if Netbox custom context has been defined. """
|
""" check if NetBox custom context has been defined. """
|
||||||
if "zabbix" in self.context:
|
if "zabbix" in self.context:
|
||||||
zabbix = self.context["zabbix"]
|
zabbix = self.context["zabbix"]
|
||||||
if "interface_type" in zabbix:
|
if "interface_type" in zabbix:
|
||||||
@ -46,7 +46,7 @@ class ZabbixInterface():
|
|||||||
""" Check if interface is type SNMP """
|
""" Check if interface is type SNMP """
|
||||||
# pylint: disable=too-many-branches
|
# pylint: disable=too-many-branches
|
||||||
if self.interface["type"] == 2:
|
if self.interface["type"] == 2:
|
||||||
# Checks if SNMP settings are defined in Netbox
|
# Checks if SNMP settings are defined in NetBox
|
||||||
if "snmp" in self.context["zabbix"]:
|
if "snmp" in self.context["zabbix"]:
|
||||||
snmp = self.context["zabbix"]["snmp"]
|
snmp = self.context["zabbix"]["snmp"]
|
||||||
self.interface["details"] = {}
|
self.interface["details"] = {}
|
||||||
@ -56,7 +56,7 @@ class ZabbixInterface():
|
|||||||
else:
|
else:
|
||||||
# Fallback to bulk enabled if not specified
|
# Fallback to bulk enabled if not specified
|
||||||
self.interface["details"]["bulk"] = "1"
|
self.interface["details"]["bulk"] = "1"
|
||||||
# SNMP Version config is required in Netbox config context
|
# SNMP Version config is required in NetBox config context
|
||||||
if snmp.get("version"):
|
if snmp.get("version"):
|
||||||
self.interface["details"]["version"] = str(snmp.pop("version"))
|
self.interface["details"]["version"] = str(snmp.pop("version"))
|
||||||
else:
|
else:
|
||||||
@ -72,7 +72,7 @@ class ZabbixInterface():
|
|||||||
community = "{$SNMP_COMMUNITY}"
|
community = "{$SNMP_COMMUNITY}"
|
||||||
self.interface["details"]["community"] = str(community)
|
self.interface["details"]["community"] = str(community)
|
||||||
# If version 3 has been used, get all
|
# If version 3 has been used, get all
|
||||||
# SNMPv3 Netbox related configs
|
# SNMPv3 NetBox related configs
|
||||||
elif self.interface["details"]["version"] == '3':
|
elif self.interface["details"]["version"] == '3':
|
||||||
items = ["securityname", "securitylevel", "authpassphrase",
|
items = ["securityname", "securitylevel", "authpassphrase",
|
||||||
"privpassphrase", "authprotocol", "privprotocol",
|
"privpassphrase", "authprotocol", "privprotocol",
|
||||||
|
@ -50,7 +50,7 @@ class VirtualMachine(PhysicalDevice):
|
|||||||
try:
|
try:
|
||||||
# Initiate interface class
|
# Initiate interface class
|
||||||
interface = ZabbixInterface(self.nb.config_context, self.ip)
|
interface = ZabbixInterface(self.nb.config_context, self.ip)
|
||||||
# Check if Netbox has device context.
|
# Check if NetBox has device context.
|
||||||
# If not fall back to old config.
|
# If not fall back to old config.
|
||||||
if interface.get_context():
|
if interface.get_context():
|
||||||
# If device is SNMP type, add aditional information.
|
# If device is SNMP type, add aditional information.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# pylint: disable=invalid-name, logging-not-lazy, too-many-locals, logging-fstring-interpolation
|
# pylint: disable=invalid-name, logging-not-lazy, too-many-locals, logging-fstring-interpolation
|
||||||
|
|
||||||
"""Netbox to Zabbix sync script."""
|
"""NetBox to Zabbix sync script."""
|
||||||
import logging
|
import logging
|
||||||
import argparse
|
import argparse
|
||||||
import ssl
|
import ssl
|
||||||
@ -45,7 +45,7 @@ lgfile = logging.FileHandler(path.join(path.dirname(
|
|||||||
lgfile.setFormatter(log_format)
|
lgfile.setFormatter(log_format)
|
||||||
lgfile.setLevel(logging.DEBUG)
|
lgfile.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
logger = logging.getLogger("Netbox-Zabbix-sync")
|
logger = logging.getLogger("NetBox-Zabbix-sync")
|
||||||
logger.addHandler(lgout)
|
logger.addHandler(lgout)
|
||||||
logger.addHandler(lgfile)
|
logger.addHandler(lgfile)
|
||||||
logger.setLevel(logging.WARNING)
|
logger.setLevel(logging.WARNING)
|
||||||
@ -80,7 +80,7 @@ def main(arguments):
|
|||||||
zabbix_host = environ.get("ZABBIX_HOST")
|
zabbix_host = environ.get("ZABBIX_HOST")
|
||||||
netbox_host = environ.get("NETBOX_HOST")
|
netbox_host = environ.get("NETBOX_HOST")
|
||||||
netbox_token = environ.get("NETBOX_TOKEN")
|
netbox_token = environ.get("NETBOX_TOKEN")
|
||||||
# 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 = hostgroup_format.split("/")
|
||||||
@ -91,11 +91,11 @@ def main(arguments):
|
|||||||
device_cfs = list(netbox.extras.custom_fields.filter(
|
device_cfs = list(netbox.extras.custom_fields.filter(
|
||||||
type="text", content_type_id=23))
|
type="text", content_type_id=23))
|
||||||
except RequestsConnectionError:
|
except RequestsConnectionError:
|
||||||
logger.error(f"Unable to connect to Netbox with URL {netbox_host}."
|
logger.error(f"Unable to connect to NetBox with URL {netbox_host}."
|
||||||
" Please check the URL and status of Netbox.")
|
" Please check the URL and status of NetBox.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
except NBRequestError as e:
|
except NBRequestError as e:
|
||||||
logger.error(f"Netbox error: {e}")
|
logger.error(f"NetBox error: {e}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
for cf in device_cfs:
|
for cf in device_cfs:
|
||||||
allowed_objects.append(cf.name)
|
allowed_objects.append(cf.name)
|
||||||
@ -129,7 +129,7 @@ def main(arguments):
|
|||||||
proxy_name = "host"
|
proxy_name = "host"
|
||||||
else:
|
else:
|
||||||
proxy_name = "name"
|
proxy_name = "name"
|
||||||
# Get all Zabbix and Netbox data
|
# Get all Zabbix and NetBox data
|
||||||
netbox_devices = list(netbox.dcim.devices.filter(**nb_device_filter))
|
netbox_devices = list(netbox.dcim.devices.filter(**nb_device_filter))
|
||||||
netbox_vms = []
|
netbox_vms = []
|
||||||
if sync_vms:
|
if sync_vms:
|
||||||
@ -153,10 +153,10 @@ def main(arguments):
|
|||||||
# Prepare list of all proxy and proxy_groups
|
# Prepare list of all proxy and proxy_groups
|
||||||
zabbix_proxy_list = proxy_prepper(zabbix_proxies, zabbix_proxygroups)
|
zabbix_proxy_list = proxy_prepper(zabbix_proxies, zabbix_proxygroups)
|
||||||
|
|
||||||
# Get Netbox API version
|
# Get NetBox API version
|
||||||
nb_version = netbox.version
|
nb_version = netbox.version
|
||||||
|
|
||||||
# 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(nb_vm, zabbix, netbox_journals, nb_version,
|
vm = VirtualMachine(nb_vm, zabbix, netbox_journals, nb_version,
|
||||||
@ -175,11 +175,11 @@ def main(arguments):
|
|||||||
if vm.status in zabbix_device_removal:
|
if vm.status in zabbix_device_removal:
|
||||||
if vm.zabbix_id:
|
if vm.zabbix_id:
|
||||||
# Delete device from Zabbix
|
# Delete device from Zabbix
|
||||||
# and remove hostID from Netbox.
|
# and remove hostID from NetBox.
|
||||||
vm.cleanup()
|
vm.cleanup()
|
||||||
logger.info(f"VM {vm.name}: cleanup complete")
|
logger.info(f"VM {vm.name}: cleanup complete")
|
||||||
continue
|
continue
|
||||||
# Device has been added to Netbox
|
# Device has been added to NetBox
|
||||||
# but is not in Activate state
|
# but is not in Activate state
|
||||||
logger.info(f"VM {vm.name}: skipping since this VM is "
|
logger.info(f"VM {vm.name}: skipping since this VM is "
|
||||||
f"not in the active state.")
|
f"not in the active state.")
|
||||||
@ -243,11 +243,11 @@ def main(arguments):
|
|||||||
if device.status in zabbix_device_removal:
|
if device.status in zabbix_device_removal:
|
||||||
if device.zabbix_id:
|
if device.zabbix_id:
|
||||||
# Delete device from Zabbix
|
# Delete device from Zabbix
|
||||||
# and remove hostID from Netbox.
|
# and remove hostID from NetBox.
|
||||||
device.cleanup()
|
device.cleanup()
|
||||||
logger.info(f"Device {device.name}: cleanup complete")
|
logger.info(f"Device {device.name}: cleanup complete")
|
||||||
continue
|
continue
|
||||||
# Device has been added to Netbox
|
# Device has been added to NetBox
|
||||||
# but is not in Activate state
|
# but is not in Activate state
|
||||||
logger.info(f"Device {device.name}: skipping since this device is "
|
logger.info(f"Device {device.name}: skipping since this device is "
|
||||||
f"not in the active state.")
|
f"not in the active state.")
|
||||||
@ -278,7 +278,7 @@ def main(arguments):
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description='A script to sync Zabbix with Netbox device data.'
|
description='A script to sync Zabbix with NetBox device data.'
|
||||||
)
|
)
|
||||||
parser.add_argument("-v", "--verbose", help="Turn on debugging.",
|
parser.add_argument("-v", "--verbose", help="Turn on debugging.",
|
||||||
action="store_true")
|
action="store_true")
|
||||||
|
Loading…
Reference in New Issue
Block a user