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_HOST="https://zabbix.local"
* ZABBIX_USER="username" * ZABBIX_USER="username"
@ -10,16 +11,33 @@ Use the following variables for the environment:
* NETBOX_HOST="https://netbox.local" * NETBOX_HOST="https://netbox.local"
* NETBOX_TOKEN="secrettoken" * 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. 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} {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: Use the following custom fields in Netbox to map the Zabbix URL:
* Type: Integer * Type: Integer
* Name: zabbix_hostid * Name: zabbix_hostid
* Required: False * Required: False
@ -33,3 +51,13 @@ And this field for the Zabbix template
* Required: False * Required: False
* Default: null * Default: null
* Object: dcim > device_type * 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...") f"but not primary. Skipping this host...")
logger.info(e) logger.info(e)
continue 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 # Checks if device is in cleanup state
if(device.status != "Active"): if(device.status != "Active"):
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"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 continue
if(device.zabbix_id): if(device.zabbix_id):
# Device is already present in Zabbix # Device is already present in Zabbix
@ -90,15 +108,13 @@ def main(arguments):
else: else:
# Add hostgroup is flag is true # Add hostgroup is flag is true
# and Hostgroup is not present in Zabbix # and Hostgroup is not present in Zabbix
if(not device.getZabbixGroup(zabbix_groups) if(not device.hostgroup and arguments.hostgroups):
and arguments.hostgroups):
# Create new hostgroup # Create new hostgroup
device.createZabbixHostgroup() device.createZabbixHostgroup()
zabbix_groups = zabbix.hostgroup.get(output=['name']) zabbix_groups = zabbix.hostgroup.get(output=['name'])
# Add device to Zabbix # Add device to Zabbix
device.createInZabbix(zabbix_groups, zabbix_templates) device.createInZabbix(zabbix_groups, zabbix_templates)
except SyncError: except SyncError:
# logger.error(e)
pass pass
@ -133,10 +149,13 @@ class NetworkDevice():
self.name = nb.name self.name = nb.name
self.status = nb.status.label self.status = nb.status.label
self.zabbix = zabbix self.zabbix = zabbix
self.hg_format = (f"{self.nb.site.name}/" self.tenant = nb.tenant
f"{self.nb.device_type.manufacturer.name}/" self.hostgroup = None
f"{self.nb.device_role.name}") self.hg_format = [self.nb.site.name,
self.nb.device_type.manufacturer.name,
self.nb.device_role.name]
self._setBasics() self._setBasics()
self.setHostgroup()
def _setBasics(self): def _setBasics(self):
""" """
@ -169,6 +188,10 @@ class NetworkDevice():
logger.warning(e) logger.warning(e)
raise SyncInventoryError(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): def isCluster(self):
""" """
Checks if device is part of cluster. Checks if device is part of cluster.
@ -215,7 +238,7 @@ class NetworkDevice():
""" """
if(not self.template_name): if(not self.template_name):
e = (f"Device template '{self.nb.device_type.display_name}' " e = (f"Device template '{self.nb.device_type.display_name}' "
"has no template defined.") "has no Zabbix template defined.")
logger.info(e) logger.info(e)
raise SyncInventoryError() raise SyncInventoryError()
for template in templates: for template in templates:
@ -239,16 +262,18 @@ class NetworkDevice():
""" """
# Go through all groups # Go through all groups
for group in groups: for group in groups:
if(group['name'] == self.hg_format): if(group['name'] == self.hostgroup):
self.group_id = group['groupid'] self.group_id = group['groupid']
e = (f"Found group ID {str(group['groupid'])} " e = (f"Found group {group['name']} for host {self.name}.")
f"for host {self.name}.") #e = (f"Found group ID {str(group['groupid'])} "
# f"for host {self.name}.")
logger.debug(e) logger.debug(e)
return True return True
else: 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.") f"for host {self.name} in Zabbix.")
logger.warning(e) logger.warning(e)
raise SyncInventoryError(e)
def cleanup(self): def cleanup(self):
""" """
@ -320,8 +345,8 @@ class NetworkDevice():
Creates Zabbix host group based on hostgroup format. Creates Zabbix host group based on hostgroup format.
""" """
try: try:
self.zabbix.hostgroup.create(name=self.hg_format) self.zabbix.hostgroup.create(name=self.hostgroup)
e = f"Added hostgroup '{self.hg_format}'." e = f"Added hostgroup '{self.hostgroup}'."
logger.info(e) logger.info(e)
except ZabbixAPIException as e: except ZabbixAPIException as e:
e = f"Couldn't add hostgroup, Zabbix returned {str(e)}." e = f"Couldn't add hostgroup, Zabbix returned {str(e)}."
@ -427,6 +452,9 @@ if(__name__ == "__main__"):
parser.add_argument("-H", "--hostgroups", parser.add_argument("-H", "--hostgroups",
help="Create Zabbix hostgroups if not present", help="Create Zabbix hostgroups if not present",
action="store_true") 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() args = parser.parse_args()
main(args) main(args)