Added Tenant flag, fixed #1. Fixed duplicate 'Hostgroup not found' log message.

This commit is contained in:
Twan Kamans 2021-04-16 21:29:07 +02:00
parent 1f38e165c6
commit b18a0c0897
2 changed files with 523 additions and 467 deletions

View File

@ -1,8 +1,9 @@
A script to sync the Netbox inventory to Zabbix.
A script to sync the Netbox device inventory to Zabbix.
Requires pyzabbix and pynetbox
## Requires pyzabbix and pynetbox.
Use the following variables for the environment:
### Script settings
#### Enviroment variables
* ZABBIX_HOST="https://zabbix.local"
* ZABBIX_USER="username"
@ -10,16 +11,33 @@ Use the following variables for the environment:
* NETBOX_HOST="https://netbox.local"
* NETBOX_TOKEN="secrettoken"
#### Flags
| Flag | Option | Description |
| ------------ | ------------ | ------------ |
| -c | cluster | For clustered devices: only add the primary node of a cluster and use the cluster name as hostname. |
| -H | hostgroup | Create non-existing hostgroups in Zabbix. Usefull for a first run to add all required hostgroups. |
| -t | tenant | Add the tenant name to the hostgroup format (Tenant/Site/Manufacturer/Role) |
| -v | Verbose | Log with debugging on. |
#### Logging
Logs are generated under sync.log, set the script for debugging / info options etc.
Important: you need to set the hostgroup in Zabbix before a sync can occur. This is in the following format:
#### Hostgroups: manual mode
In case of omitting the -H flag, manual hostgroup creation is required for devices in a new category.
This is in the format:
{Site name}/{Manufacturer name}/{Device role name}
And with tenants (-t flag):
{Tenant name}/{Site name}/{Manufacturer name}/{Device role name}
Make sure that the Zabbix user has proper permissions to create hosts
Make sure that the Zabbix user has proper permissions to create hosts.
The hostgroups are in a nested format. This means that proper permissions only need to be applied to the site name hostgroup and cascaded to any child hostgroups.
### Netbox settings
#### Custom fields
Use the following custom fields in Netbox to map the Zabbix URL:
* Type: Integer
* Name: zabbix_hostid
* Required: False
@ -33,3 +51,13 @@ And this field for the Zabbix template
* Required: False
* Default: null
* Object: dcim > device_type
#### Permissions
Make sure that the user has proper permissions for device read and modify (modify to set the Zabbix HostID custom field) operations.
#### Custom links
To make the user experience easier you could add a custom link that redirects users to the Zabbix latest data.
* Name: zabbix_latestData
* Text: {% if obj.cf["zabbix_hostid"] %}Show host in Zabbix{% endif %}
* URL: {ZABBIX_URL} /zabbix.php?action=latest.view&filter_hostids[]={{ obj.cf["zabbix_hostid"] }}&filter_application=&filter_select=&filter_set=1

View File

@ -78,11 +78,29 @@ def main(arguments):
f"but not primary. Skipping this host...")
logger.info(e)
continue
# With -t flag: add Tenant name to hostgroup name.
if(arguments.tenant):
if(device.tenant):
device.hg_format.insert(1, device.tenant.name)
device.setHostgroup()
logger.debug(f"Added Tenant {device.tenant.name} to "
f"hostgroup format of {device.name}.")
else:
logger.debug(f"{device.name} is not linked to a tenant. "
f"Using HG format '{device.hostgroup}'.")
# Checks if device is in cleanup state
if(device.status != "Active"):
# Delete device from Zabbix
# and remove hostID from Netbox.
device.cleanup()
if(device.zabbix_id):
# Delete device from Zabbix
# and remove hostID from Netbox.
device.cleanup()
logger.info(f"Cleaned up host {device.name}.")
else:
# Device has been added to Netbox
# but is not in Activate state
logger.info(f"Skipping host {device.name} since its "
f"not in the active state.")
continue
if(device.zabbix_id):
# Device is already present in Zabbix
@ -90,15 +108,13 @@ def main(arguments):
else:
# Add hostgroup is flag is true
# and Hostgroup is not present in Zabbix
if(not device.getZabbixGroup(zabbix_groups)
and arguments.hostgroups):
if(not device.hostgroup and arguments.hostgroups):
# Create new hostgroup
device.createZabbixHostgroup()
zabbix_groups = zabbix.hostgroup.get(output=['name'])
# Add device to Zabbix
device.createInZabbix(zabbix_groups, zabbix_templates)
except SyncError:
# logger.error(e)
pass
@ -133,10 +149,13 @@ class NetworkDevice():
self.name = nb.name
self.status = nb.status.label
self.zabbix = zabbix
self.hg_format = (f"{self.nb.site.name}/"
f"{self.nb.device_type.manufacturer.name}/"
f"{self.nb.device_role.name}")
self.tenant = nb.tenant
self.hostgroup = None
self.hg_format = [self.nb.site.name,
self.nb.device_type.manufacturer.name,
self.nb.device_role.name]
self._setBasics()
self.setHostgroup()
def _setBasics(self):
"""
@ -169,6 +188,10 @@ class NetworkDevice():
logger.warning(e)
raise SyncInventoryError(e)
def setHostgroup(self):
"""Sets hostgroup to a string with hg_format parameters."""
self.hostgroup = "/".join(self.hg_format)
def isCluster(self):
"""
Checks if device is part of cluster.
@ -215,7 +238,7 @@ class NetworkDevice():
"""
if(not self.template_name):
e = (f"Device template '{self.nb.device_type.display_name}' "
"has no template defined.")
"has no Zabbix template defined.")
logger.info(e)
raise SyncInventoryError()
for template in templates:
@ -239,16 +262,18 @@ class NetworkDevice():
"""
# Go through all groups
for group in groups:
if(group['name'] == self.hg_format):
if(group['name'] == self.hostgroup):
self.group_id = group['groupid']
e = (f"Found group ID {str(group['groupid'])} "
f"for host {self.name}.")
e = (f"Found group {group['name']} for host {self.name}.")
#e = (f"Found group ID {str(group['groupid'])} "
# f"for host {self.name}.")
logger.debug(e)
return True
else:
e = (f"Unable to find group '{self.hg_format}' "
e = (f"Unable to find group '{self.hostgroup}' "
f"for host {self.name} in Zabbix.")
logger.warning(e)
raise SyncInventoryError(e)
def cleanup(self):
"""
@ -320,8 +345,8 @@ class NetworkDevice():
Creates Zabbix host group based on hostgroup format.
"""
try:
self.zabbix.hostgroup.create(name=self.hg_format)
e = f"Added hostgroup '{self.hg_format}'."
self.zabbix.hostgroup.create(name=self.hostgroup)
e = f"Added hostgroup '{self.hostgroup}'."
logger.info(e)
except ZabbixAPIException as e:
e = f"Couldn't add hostgroup, Zabbix returned {str(e)}."
@ -427,6 +452,9 @@ if(__name__ == "__main__"):
parser.add_argument("-H", "--hostgroups",
help="Create Zabbix hostgroups if not present",
action="store_true")
parser.add_argument("-t", "--tenant", action="store_true",
help=("Add Tenant name to the Zabbix "
"hostgroup name scheme."))
args = parser.parse_args()
main(args)