mirror of
https://github.com/TheNetworkGuy/netbox-zabbix-sync.git
synced 2025-07-16 12:12:57 -06:00
First working version of clustered IP monitoring
This commit is contained in:
parent
a0ea21d731
commit
f13b86e2e0
23
README.md
23
README.md
@ -70,18 +70,26 @@ ZABBIX_TOKEN=othersecrettoken
|
|||||||
### Netbox custom fields
|
### Netbox custom fields
|
||||||
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):
|
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):
|
||||||
```
|
```
|
||||||
* Type: Integer
|
|
||||||
* Name: zabbix_hostid
|
* Name: zabbix_hostid
|
||||||
|
* Type: Integer
|
||||||
* Required: False
|
* Required: False
|
||||||
* Default: null
|
* Default: null
|
||||||
* Object: dcim > device
|
* Content types: DCIM > Device, DCIM > Virtual Chassis (optional)
|
||||||
```
|
```
|
||||||
```
|
```
|
||||||
* Type: Text
|
|
||||||
* Name: zabbix_template
|
* Name: zabbix_template
|
||||||
|
* Type: Text
|
||||||
* Required: False
|
* Required: False
|
||||||
* Default: null
|
* Default: null
|
||||||
* Object: dcim > device_type
|
* Content types: DCIM > Device Type
|
||||||
|
```
|
||||||
|
```
|
||||||
|
* Name: primary_ip
|
||||||
|
* Type: Object
|
||||||
|
* Object type: IPAM > IP Address
|
||||||
|
* Required: False
|
||||||
|
* Default: null
|
||||||
|
* Content types: DCIM > Virtual Chassis
|
||||||
```
|
```
|
||||||
You can make the `zabbix_hostid` field hidden or read-only to prevent human intervention.
|
You can make the `zabbix_hostid` field hidden or read-only to prevent human intervention.
|
||||||
|
|
||||||
@ -191,6 +199,13 @@ See `config.py.example` for an extensive example map.
|
|||||||
Any Zabix Inventory fields that are not included in the map will not be touched by the script,
|
Any Zabix Inventory fields that are not included in the map will not be touched by the script,
|
||||||
so you can safely add manual values or use items to automatically add values to other fields.
|
so you can safely add manual values or use items to automatically add values to other fields.
|
||||||
|
|
||||||
|
### Clustering
|
||||||
|
When `clustering` is set to `True`, the script will monitor devices within a a Virtual Chassis through the master device.
|
||||||
|
If needed, when `clusterip` is set to `True`, the script will try to use the custom field name defined in `clusterip_cf` on the Virtual Chassis
|
||||||
|
as the primary IP for the Master device. This is only true when the master device has no primary ip set.
|
||||||
|
|
||||||
|
Setting this up allows you to use FHRP group assigned floating IPs to monitor your clusters which can be useful for a variety of vendors.
|
||||||
|
|
||||||
### Template source
|
### Template source
|
||||||
You can either use a Netbox device type custom field or Netbox config context for the Zabbix template information.
|
You can either use a Netbox device type custom field or Netbox config context for the Zabbix template information.
|
||||||
|
|
||||||
|
@ -15,6 +15,11 @@ device_cf = "zabbix_hostid"
|
|||||||
## Enable clustering of devices with virtual chassis setup
|
## Enable clustering of devices with virtual chassis setup
|
||||||
clustering = False
|
clustering = False
|
||||||
|
|
||||||
|
# Allow monitoring on shared cluster IP if no primary IP has been set on the master device.
|
||||||
|
# clusterip_cf specifies which custom field contains the IP address object in the Virtual Chassis.
|
||||||
|
clusterip = False
|
||||||
|
clusterip_cf = "primary_ip"
|
||||||
|
|
||||||
## Enable hostgroup generation. Requires permissions in Zabbix
|
## Enable hostgroup generation. Requires permissions in Zabbix
|
||||||
create_hostgroups = True
|
create_hostgroups = True
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ from modules.tools import build_path
|
|||||||
try:
|
try:
|
||||||
from config import (
|
from config import (
|
||||||
template_cf, device_cf,
|
template_cf, device_cf,
|
||||||
|
clustering, clusterip, clusterip_cf,
|
||||||
traverse_site_groups,
|
traverse_site_groups,
|
||||||
traverse_regions,
|
traverse_regions,
|
||||||
inventory_sync,
|
inventory_sync,
|
||||||
@ -31,7 +32,7 @@ class NetworkDevice():
|
|||||||
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, nb_vcs, journal=None, logger=None):
|
||||||
self.nb = nb
|
self.nb = nb
|
||||||
self.id = nb.id
|
self.id = nb.id
|
||||||
self.name = nb.name
|
self.name = nb.name
|
||||||
@ -53,18 +54,27 @@ class NetworkDevice():
|
|||||||
self.inventory_mode = -1
|
self.inventory_mode = -1
|
||||||
self.inventory = {}
|
self.inventory = {}
|
||||||
self.logger = logger if logger else getLogger(__name__)
|
self.logger = logger if logger else getLogger(__name__)
|
||||||
self._setBasics()
|
self._setBasics(nb_vcs)
|
||||||
|
|
||||||
def _setBasics(self):
|
def _setBasics(self,nb_vcs):
|
||||||
"""
|
"""
|
||||||
Sets basic information like IP address.
|
Sets basic information like IP address.
|
||||||
"""
|
"""
|
||||||
# Return error if device does not have primary IP.
|
self.ip = None
|
||||||
if self.nb.primary_ip:
|
if self.nb.primary_ip:
|
||||||
self.cidr = self.nb.primary_ip.address
|
self.cidr = self.nb.primary_ip.address
|
||||||
self.ip = self.cidr.split("/")[0]
|
self.ip = self.cidr.split("/")[0]
|
||||||
else:
|
elif bool(self.nb.virtual_chassis) and clustering and clusterip:
|
||||||
e = f"Device {self.name}: no primary IP."
|
devcluster = next((vc for vc in nb_vcs if vc['id'] == self.nb.virtual_chassis.id), None)
|
||||||
|
if (devcluster and clusterip_cf in devcluster['custom_fields'] and
|
||||||
|
devcluster['custom_fields'][clusterip_cf]):
|
||||||
|
self.ip = devcluster['custom_fields'][clusterip_cf]['address'].split("/")[0]
|
||||||
|
self.logger.debug(f"Device {self.name} is cluster member, "
|
||||||
|
f"using primary cluster ip address {self.ip}.")
|
||||||
|
|
||||||
|
if not self.ip:
|
||||||
|
# Return error if device does not have primary IP.
|
||||||
|
e = f"Device {self.name} no primary IP, skipping this device."
|
||||||
self.logger.info(e)
|
self.logger.info(e)
|
||||||
raise SyncInventoryError(e)
|
raise SyncInventoryError(e)
|
||||||
|
|
||||||
@ -262,7 +272,7 @@ class NetworkDevice():
|
|||||||
raise SyncInventoryError(e)
|
raise SyncInventoryError(e)
|
||||||
return self.nb.virtual_chassis.master.id
|
return self.nb.virtual_chassis.master.id
|
||||||
|
|
||||||
def promoteMasterDevice(self):
|
def promoteMasterDevice(self,vcs):
|
||||||
"""
|
"""
|
||||||
If device is Primary in cluster,
|
If device is Primary in cluster,
|
||||||
promote device name to the cluster name.
|
promote device name to the cluster name.
|
||||||
|
@ -19,7 +19,8 @@ try:
|
|||||||
zabbix_device_removal,
|
zabbix_device_removal,
|
||||||
zabbix_device_disable,
|
zabbix_device_disable,
|
||||||
hostgroup_format,
|
hostgroup_format,
|
||||||
nb_device_filter
|
nb_device_filter,
|
||||||
|
device_cf
|
||||||
)
|
)
|
||||||
except ModuleNotFoundError:
|
except ModuleNotFoundError:
|
||||||
print("Configuration file config.py not found in main directory."
|
print("Configuration file config.py not found in main directory."
|
||||||
@ -107,6 +108,7 @@ def main(arguments):
|
|||||||
# Get all Zabbix and Netbox data
|
# Get all Zabbix and Netbox data
|
||||||
netbox_devices = netbox.dcim.devices.filter(**nb_device_filter)
|
netbox_devices = netbox.dcim.devices.filter(**nb_device_filter)
|
||||||
netbox_site_groups = convert_recordset((netbox.dcim.site_groups.all()))
|
netbox_site_groups = convert_recordset((netbox.dcim.site_groups.all()))
|
||||||
|
netbox_virtual_chassis = convert_recordset((netbox.dcim.virtual_chassis.all()))
|
||||||
netbox_regions = convert_recordset(netbox.dcim.regions.all())
|
netbox_regions = convert_recordset(netbox.dcim.regions.all())
|
||||||
netbox_journals = netbox.extras.journal_entries
|
netbox_journals = netbox.extras.journal_entries
|
||||||
zabbix_groups = zabbix.hostgroup.get(output=['groupid', 'name'])
|
zabbix_groups = zabbix.hostgroup.get(output=['groupid', 'name'])
|
||||||
@ -130,21 +132,44 @@ def main(arguments):
|
|||||||
for nb_device in netbox_devices:
|
for nb_device in netbox_devices:
|
||||||
try:
|
try:
|
||||||
# Set device instance set data such as hostgroup and template information.
|
# Set device instance set data such as hostgroup and template information.
|
||||||
|
vcobj = None
|
||||||
device = NetworkDevice(nb_device, zabbix, netbox_journals, nb_version,
|
device = NetworkDevice(nb_device, zabbix, netbox_journals, nb_version,
|
||||||
create_journal, logger)
|
netbox_virtual_chassis, create_journal, logger)
|
||||||
device.set_hostgroup(hostgroup_format,netbox_site_groups,netbox_regions)
|
device.set_hostgroup(hostgroup_format,netbox_site_groups,netbox_regions)
|
||||||
device.set_template(templates_config_context, templates_config_context_overrule)
|
device.set_template(templates_config_context, templates_config_context_overrule)
|
||||||
device.set_inventory(nb_device)
|
device.set_inventory(nb_device)
|
||||||
# Checks if device is part of cluster.
|
# Checks if device is part of cluster.
|
||||||
# Requires clustering variable
|
# Requires clustering variable
|
||||||
if device.isCluster() and clustering:
|
if device.isCluster() and clustering:
|
||||||
|
vcobj = netbox.dcim.virtual_chassis.get(id=device.nb.virtual_chassis.id)
|
||||||
# Check if device is primary or secondary
|
# Check if device is primary or secondary
|
||||||
if device.promoteMasterDevice():
|
if device.promoteMasterDevice(netbox_virtual_chassis):
|
||||||
e = (f"Device {device.name}: is "
|
e = (f"Device {device.name}: is "
|
||||||
f"part of cluster and primary.")
|
f"part of cluster and primary.")
|
||||||
logger.info(e)
|
logger.info(e)
|
||||||
|
# If the device has a Zabbix host ID, make sure to assign it
|
||||||
|
# to the Virtual Chassis if that is not yet the case.
|
||||||
|
if device.zabbix_id and not vcobj.custom_fields[device_cf]:
|
||||||
|
logger.debug(f"Assigning Zabbix host id {device.zabbix_id}"
|
||||||
|
f" to Virtual Chassis {device.nb.virtual_chassis['name']}.")
|
||||||
|
vcobj.custom_fields[device_cf] = int(device.zabbix_id)
|
||||||
|
vcobj.save()
|
||||||
|
# If the device does not have a Zabbix host ID, use the Virtual Cluster Zabbix ID
|
||||||
|
elif vcobj.custom_fields[device_cf] and not device.zabbix_id:
|
||||||
|
logger.debug(f"Assigning Zabbix host id {vcobj.custom_fields[device_cf]}"
|
||||||
|
f" to device {device.nb.name} based on cluster membership.")
|
||||||
|
device.zabbix_id = int(vcobj.custom_fields[device_cf])
|
||||||
|
device.nb.custom_fields[device_cf] = device.zabbix_id
|
||||||
|
device.nb.save()
|
||||||
else:
|
else:
|
||||||
# Device is secondary in cluster.
|
# if the device has a Zabbix host ID but is no longer master of the Virtual Chassis,
|
||||||
|
# make sure to cleanup the host ID.
|
||||||
|
if vcobj.custom_fields[device_cf] and device.zabbix_id:
|
||||||
|
logger.debug(f"Device {device.name} cleanup old Zabbix host ID"
|
||||||
|
"{device.zabbix_id} artefact in NetBox.")
|
||||||
|
device.nb.custom_fields[device_cf] = None
|
||||||
|
device.nb.save()
|
||||||
|
# Device is not primary in cluster.
|
||||||
# Don't continue with this device.
|
# Don't continue with this device.
|
||||||
e = (f"Device {device.name}: is part of cluster "
|
e = (f"Device {device.name}: is part of cluster "
|
||||||
f"but not primary. Skipping this host...")
|
f"but not primary. Skipping this host...")
|
||||||
@ -183,6 +208,12 @@ def main(arguments):
|
|||||||
# Add device to Zabbix
|
# Add device to Zabbix
|
||||||
device.createInZabbix(zabbix_groups, zabbix_templates,
|
device.createInZabbix(zabbix_groups, zabbix_templates,
|
||||||
zabbix_proxy_list)
|
zabbix_proxy_list)
|
||||||
|
# Make sure to save hostid in Virtual Chassis as well
|
||||||
|
if device.isCluster() and clustering and device.zabbix_id:
|
||||||
|
logger.debug(f"Assigning Zabbix host id {device.zabbix_id}"
|
||||||
|
f" to Virtual Cluster {vcobj.name}")
|
||||||
|
vcobj.custom_fields[device_cf] = int(device.zabbix_id)
|
||||||
|
vcobj.save()
|
||||||
except SyncError:
|
except SyncError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user