Added basic VM support

This commit is contained in:
TheNetworkGuy 2024-10-25 18:46:20 +02:00
parent e827953d8d
commit 9f29d2b27b
5 changed files with 157 additions and 78 deletions

View File

@ -21,6 +21,13 @@ create_hostgroups = True
## Create journal entries ## Create journal entries
create_journal = False create_journal = False
## Virtual machine sync
# Set sync_vms to True in order to use this new feature
# Use the hostgroup vm_hostgroup_format mapper for specific
# hostgroup atributes of VM's such as cluster_type and cluster
sync_vms = False
vm_hostgroup_format = "cluster_type/cluster/role"
## Proxy Sync ## Proxy Sync
# Set to true to enable removal of proxy's under hosts. Use with caution and make sure that you specified # Set to true to enable removal of proxy's under hosts. Use with caution and make sure that you specified
# all the required proxy's in the device config context before enabeling this option. # all the required proxy's in the device config context before enabeling this option.

View File

@ -24,7 +24,7 @@ except ModuleNotFoundError:
"Please create the file or rename the config.py.example file to config.py.") "Please create the file or rename the config.py.example file to config.py.")
sys.exit(0) sys.exit(0)
class NetworkDevice(): class PhysicalDevice():
# pylint: disable=too-many-instance-attributes, too-many-arguments # pylint: disable=too-many-instance-attributes, too-many-arguments
""" """
Represents Network device. Represents Network device.
@ -55,6 +55,12 @@ class NetworkDevice():
self.logger = logger if logger else getLogger(__name__) self.logger = logger if logger else getLogger(__name__)
self._setBasics() self._setBasics()
def __repr__(self):
return self.name
def __str__(self):
return self.__repr__()
def _setBasics(self): def _setBasics(self):
""" """
Sets basic information like IP address. Sets basic information like IP address.
@ -64,7 +70,7 @@ class NetworkDevice():
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: else:
e = f"Device {self.name}: no primary IP." e = f"Host {self.name}: no primary IP."
self.logger.info(e) self.logger.info(e)
raise SyncInventoryError(e) raise SyncInventoryError(e)
@ -72,7 +78,7 @@ class NetworkDevice():
if device_cf in self.nb.custom_fields: if device_cf in self.nb.custom_fields:
self.zabbix_id = self.nb.custom_fields[device_cf] self.zabbix_id = self.nb.custom_fields[device_cf]
else: else:
e = f"Device {self.name}: Custom field {device_cf} not present" e = f"Host {self.name}: Custom field {device_cf} not present"
self.logger.warning(e) self.logger.warning(e)
raise SyncInventoryError(e) raise SyncInventoryError(e)
@ -83,7 +89,7 @@ class NetworkDevice():
self.name = f"NETBOX_ID{self.id}" self.name = f"NETBOX_ID{self.id}"
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"Device {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:
@ -153,7 +159,7 @@ class NetworkDevice():
# 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"Device {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":
@ -161,12 +167,12 @@ class NetworkDevice():
elif inventory_mode == "automatic": elif inventory_mode == "automatic":
self.inventory_mode = 1 self.inventory_mode = 1
else: else:
self.logger.error(f"Device {self.name}: Specified value for inventory mode in" self.logger.error(f"Host {self.name}: Specified value for inventory mode in"
f" config is not valid. Got value {inventory_mode}") f" config is not valid. Got value {inventory_mode}")
return False return False
self.inventory = {} self.inventory = {}
if inventory_sync and self.inventory_mode in [0,1]: if inventory_sync and self.inventory_mode in [0,1]:
self.logger.debug(f"Device {self.name}: Starting inventory mapper") self.logger.debug(f"Host {self.name}: Starting inventory mapper")
# Let's build an inventory dict for each property in the inventory_map # Let's build an inventory dict for each property in the inventory_map
for nb_inv_field, zbx_inv_field in inventory_map.items(): for nb_inv_field, zbx_inv_field in inventory_map.items():
field_list = nb_inv_field.split("/") # convert str to list based on delimiter field_list = nb_inv_field.split("/") # convert str to list based on delimiter
@ -183,14 +189,14 @@ class NetworkDevice():
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"Device {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:
# Value is not a string or numeral, probably not what the user expected. # Value is not a string or numeral, probably not what the user expected.
self.logger.error(f"Device {self.name}: Inventory lookup for '{nb_inv_field}'" self.logger.error(f"Host {self.name}: Inventory lookup for '{nb_inv_field}'"
" returned an unexpected type: it will be skipped.") " returned an unexpected type: it will be skipped.")
self.logger.debug(f"Device {self.name}: Inventory mapping complete. " self.logger.debug(f"Host {self.name}: Inventory mapping complete. "
f"Mapped {len(list(filter(None, self.inventory.values())))} field(s)") f"Mapped {len(list(filter(None, self.inventory.values())))} field(s)")
return True return True
@ -224,12 +230,12 @@ class NetworkDevice():
""" """
masterid = self.getClusterMaster() masterid = self.getClusterMaster()
if masterid == self.id: if masterid == self.id:
self.logger.debug(f"Device {self.name} is primary cluster member. " self.logger.debug(f"Host {self.name} is primary cluster member. "
f"Modifying hostname from {self.name} to " + f"Modifying hostname from {self.name} to " +
f"{self.nb.virtual_chassis.name}.") f"{self.nb.virtual_chassis.name}.")
self.name = self.nb.virtual_chassis.name self.name = self.nb.virtual_chassis.name
return True return True
self.logger.debug(f"Device {self.name} is non-primary cluster member.") self.logger.debug(f"Host {self.name} is non-primary cluster member.")
return False return False
def zbxTemplatePrepper(self, templates): def zbxTemplatePrepper(self, templates):
@ -240,7 +246,7 @@ class NetworkDevice():
""" """
# Check if there are templates defined # Check if there are templates defined
if not self.zbx_template_names: if not self.zbx_template_names:
e = f"Device {self.name}: No templates found" e = f"Host {self.name}: No templates found"
self.logger.info(e) self.logger.info(e)
raise SyncInventoryError() raise SyncInventoryError()
# Set variable to empty list # Set variable to empty list
@ -257,7 +263,7 @@ class NetworkDevice():
template_match = True template_match = True
self.zbx_templates.append({"templateid": zbx_template['templateid'], self.zbx_templates.append({"templateid": zbx_template['templateid'],
"name": zbx_template['name']}) "name": zbx_template['name']})
e = f"Device {self.name}: found template {zbx_template['name']}" e = f"Host {self.name}: found template {zbx_template['name']}"
self.logger.debug(e) self.logger.debug(e)
# Return error should the template not be found in Zabbix # Return error should the template not be found in Zabbix
if not template_match: if not template_match:
@ -276,7 +282,7 @@ class NetworkDevice():
for group in groups: for group in groups:
if group['name'] == self.hostgroup: if group['name'] == self.hostgroup:
self.group_id = group['groupid'] self.group_id = group['groupid']
e = f"Device {self.name}: matched group {group['name']}" e = f"Host {self.name}: matched group {group['name']}"
self.logger.debug(e) self.logger.debug(e)
return True return True
return False return False
@ -291,7 +297,7 @@ class NetworkDevice():
self.zabbix.host.delete(self.zabbix_id) self.zabbix.host.delete(self.zabbix_id)
self.nb.custom_fields[device_cf] = None self.nb.custom_fields[device_cf] = None
self.nb.save() self.nb.save()
e = f"Device {self.name}: Deleted host from Zabbix." e = f"Host {self.name}: Deleted host from Zabbix."
self.logger.info(e) self.logger.info(e)
self.create_journal_entry("warning", "Deleted host from Zabbix") self.create_journal_entry("warning", "Deleted host from Zabbix")
except APIRequestError as e: except APIRequestError as e:
@ -364,11 +370,11 @@ class NetworkDevice():
continue continue
# If the proxy name matches # If the proxy name matches
if proxy["name"] == proxy_name: if proxy["name"] == proxy_name:
self.logger.debug(f"Device {self.name}: using {proxy['type']}" self.logger.debug(f"Host {self.name}: using {proxy['type']}"
f" {proxy_name}") f" {proxy_name}")
self.zbxproxy = proxy self.zbxproxy = proxy
return True return True
self.logger.warning(f"Device {self.name}: unable to find proxy {proxy_name}") self.logger.warning(f"Host {self.name}: unable to find proxy {proxy_name}")
return False return False
def createInZabbix(self, groups, templates, proxies, def createInZabbix(self, groups, templates, proxies,
@ -419,17 +425,17 @@ class NetworkDevice():
host = self.zabbix.host.create(**create_data) host = self.zabbix.host.create(**create_data)
self.zabbix_id = host["hostids"][0] self.zabbix_id = host["hostids"][0]
except APIRequestError as e: except APIRequestError as e:
e = f"Device {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"Device {self.name}: Created host in Zabbix." msg = f"Host {self.name}: Created host in Zabbix."
self.logger.info(msg) self.logger.info(msg)
self.create_journal_entry("success", msg) self.create_journal_entry("success", msg)
else: else:
e = f"Device {self.name}: Unable to add to Zabbix. Host already present." e = f"Host {self.name}: Unable to add to Zabbix. Host already present."
self.logger.warning(e) self.logger.warning(e)
def createZabbixHostgroup(self, hostgroups): def createZabbixHostgroup(self, hostgroups):
@ -478,7 +484,7 @@ class NetworkDevice():
try: try:
self.zabbix.host.update(hostid=self.zabbix_id, **kwargs) self.zabbix.host.update(hostid=self.zabbix_id, **kwargs)
except APIRequestError as e: except APIRequestError as e:
e = (f"Device {self.name}: Unable to update. " e = (f"Host {self.name}: Unable to update. "
f"Zabbix returned the following error: {str(e)}.") f"Zabbix returned the following error: {str(e)}.")
self.logger.error(e) self.logger.error(e)
raise SyncExternalError(e) from None raise SyncExternalError(e) from None
@ -502,7 +508,7 @@ class NetworkDevice():
if not self.group_id: if not self.group_id:
# Function returns true / false but also sets GroupID # Function returns true / false but also sets GroupID
if not self.setZabbixGroupID(groups) and not create_hostgroups: if not self.setZabbixGroupID(groups) and not create_hostgroups:
e = (f"Device {self.name}: different hostgroup is required but " e = (f"Host {self.name}: different hostgroup is required but "
"unable to create hostgroup without generation permission.") "unable to create hostgroup without generation permission.")
self.logger.warning(e) self.logger.warning(e)
raise SyncInventoryError(e) raise SyncInventoryError(e)
@ -523,30 +529,30 @@ class NetworkDevice():
self.logger.error(e) self.logger.error(e)
raise SyncInventoryError(e) raise SyncInventoryError(e)
if len(host) == 0: if len(host) == 0:
e = (f"Device {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]
if host["host"] == self.name: if host["host"] == self.name:
self.logger.debug(f"Device {self.name}: hostname in-sync.") self.logger.debug(f"Host {self.name}: hostname in-sync.")
else: else:
self.logger.warning(f"Device {self.name}: hostname OUT of sync. " self.logger.warning(f"Host {self.name}: hostname OUT of sync. "
f"Received value: {host['host']}") f"Received value: {host['host']}")
self.updateZabbixHost(host=self.name) self.updateZabbixHost(host=self.name)
# Execute check depending on wether the name is special or not # Execute check depending on wether the name is special or not
if self.use_visible_name: if self.use_visible_name:
if host["name"] == self.visible_name: if host["name"] == self.visible_name:
self.logger.debug(f"Device {self.name}: visible name in-sync.") self.logger.debug(f"Host {self.name}: visible name in-sync.")
else: else:
self.logger.warning(f"Device {self.name}: visible name OUT of sync." self.logger.warning(f"Host {self.name}: visible name OUT of sync."
f" Received value: {host['name']}") f" Received value: {host['name']}")
self.updateZabbixHost(name=self.visible_name) self.updateZabbixHost(name=self.visible_name)
# Check if the templates are in-sync # Check if the templates are in-sync
if not self.zbx_template_comparer(host["parentTemplates"]): if not self.zbx_template_comparer(host["parentTemplates"]):
self.logger.warning(f"Device {self.name}: template(s) OUT of sync.") self.logger.warning(f"Host {self.name}: template(s) OUT of sync.")
# Prepare Templates for API parsing # Prepare Templates for API parsing
templateids = [] templateids = []
for template in self.zbx_templates: for template in self.zbx_templates:
@ -555,33 +561,33 @@ class NetworkDevice():
self.updateZabbixHost(templates_clear=host["parentTemplates"], self.updateZabbixHost(templates_clear=host["parentTemplates"],
templates=templateids) templates=templateids)
else: else:
self.logger.debug(f"Device {self.name}: template(s) in-sync.") self.logger.debug(f"Host {self.name}: template(s) in-sync.")
for group in host["groups"]: for group in host["groups"]:
if group["groupid"] == self.group_id: if group["groupid"] == self.group_id:
self.logger.debug(f"Device {self.name}: hostgroup in-sync.") self.logger.debug(f"Host {self.name}: hostgroup in-sync.")
break break
else: else:
self.logger.warning(f"Device {self.name}: hostgroup OUT of sync.") self.logger.warning(f"Host {self.name}: hostgroup OUT of sync.")
self.updateZabbixHost(groups={'groupid': self.group_id}) self.updateZabbixHost(groups={'groupid': self.group_id})
if int(host["status"]) == self.zabbix_state: if int(host["status"]) == self.zabbix_state:
self.logger.debug(f"Device {self.name}: status in-sync.") self.logger.debug(f"Host {self.name}: status in-sync.")
else: else:
self.logger.warning(f"Device {self.name}: status OUT of sync.") self.logger.warning(f"Host {self.name}: status OUT of sync.")
self.updateZabbixHost(status=str(self.zabbix_state)) self.updateZabbixHost(status=str(self.zabbix_state))
# Check if a proxy has been defined # Check if a proxy has been defined
if self.zbxproxy: if self.zbxproxy:
# Check if proxy or proxy group is defined # Check if proxy or proxy group is defined
if (self.zbxproxy["idtype"] in host and if (self.zbxproxy["idtype"] in host and
host[self.zbxproxy["idtype"]] == self.zbxproxy["id"]): host[self.zbxproxy["idtype"]] == self.zbxproxy["id"]):
self.logger.debug(f"Device {self.name}: proxy in-sync.") self.logger.debug(f"Host {self.name}: proxy in-sync.")
# Backwards compatibility for Zabbix <= 6 # Backwards compatibility for Zabbix <= 6
elif "proxy_hostid" in host and host["proxy_hostid"] == self.zbxproxy["id"]: elif "proxy_hostid" in host and host["proxy_hostid"] == self.zbxproxy["id"]:
self.logger.debug(f"Device {self.name}: proxy in-sync.") self.logger.debug(f"Host {self.name}: proxy in-sync.")
# Proxy does not match, update Zabbix # Proxy does not match, update Zabbix
else: else:
self.logger.warning(f"Device {self.name}: proxy OUT of sync.") self.logger.warning(f"Host {self.name}: proxy OUT of sync.")
# Zabbix <= 6 patch # Zabbix <= 6 patch
if not str(self.zabbix.version).startswith('7'): if not str(self.zabbix.version).startswith('7'):
self.updateZabbixHost(proxy_hostid=self.zbxproxy['id']) self.updateZabbixHost(proxy_hostid=self.zbxproxy['id'])
@ -601,7 +607,7 @@ class NetworkDevice():
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"Device {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)
@ -614,24 +620,24 @@ class NetworkDevice():
# Checks if a proxy has been defined in Zabbix and if proxy_power config has been set # Checks if a proxy has been defined in Zabbix and if proxy_power config has been set
if proxy_set and not proxy_power: if proxy_set and not proxy_power:
# Display error message # Display error message
self.logger.error(f"Device {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:
self.logger.debug(f"Device {self.name}: proxy in-sync.") self.logger.debug(f"Host {self.name}: proxy in-sync.")
# Check host inventory mode # Check host inventory mode
if str(host['inventory_mode']) == str(self.inventory_mode): if str(host['inventory_mode']) == str(self.inventory_mode):
self.logger.debug(f"Device {self.name}: inventory_mode in-sync.") self.logger.debug(f"Host {self.name}: inventory_mode in-sync.")
else: else:
self.logger.warning(f"Device {self.name}: inventory_mode OUT of sync.") self.logger.warning(f"Host {self.name}: inventory_mode OUT of sync.")
self.updateZabbixHost(inventory_mode=str(self.inventory_mode)) self.updateZabbixHost(inventory_mode=str(self.inventory_mode))
if inventory_sync and self.inventory_mode in [0,1]: if inventory_sync and self.inventory_mode in [0,1]:
# Check host inventory mapping # Check host inventory mapping
if host['inventory'] == self.inventory: if host['inventory'] == self.inventory:
self.logger.debug(f"Device {self.name}: inventory in-sync.") self.logger.debug(f"Host {self.name}: inventory in-sync.")
else: else:
self.logger.warning(f"Device {self.name}: inventory OUT of sync.") self.logger.warning(f"Host {self.name}: inventory OUT of sync.")
self.updateZabbixHost(inventory=self.inventory) self.updateZabbixHost(inventory=self.inventory)
# If only 1 interface has been found # If only 1 interface has been found
@ -669,10 +675,10 @@ class NetworkDevice():
updates[key] = item updates[key] = item
if updates: if updates:
# If interface updates have been found: push to Zabbix # If interface updates have been found: push to Zabbix
self.logger.warning(f"Device {self.name}: Interface OUT of sync.") self.logger.warning(f"Host {self.name}: Interface OUT of sync.")
if "type" in updates: if "type" in updates:
# Changing interface type not supported. Raise exception. # Changing interface type not supported. Raise exception.
e = (f"Device {self.name}: changing interface type to " e = (f"Host {self.name}: changing interface type to "
f"{str(updates['type'])} is not supported.") f"{str(updates['type'])} is not supported.")
self.logger.error(e) self.logger.error(e)
raise InterfaceConfigError(e) raise InterfaceConfigError(e)
@ -681,7 +687,7 @@ class NetworkDevice():
try: try:
# API call to Zabbix # API call to Zabbix
self.zabbix.hostinterface.update(updates) self.zabbix.hostinterface.update(updates)
e = f"Device {self.name}: solved interface conflict." e = f"Host {self.name}: solved interface conflict."
self.logger.info(e) self.logger.info(e)
self.create_journal_entry("info", e) self.create_journal_entry("info", e)
except APIRequestError as e: except APIRequestError as e:
@ -690,10 +696,10 @@ class NetworkDevice():
raise SyncExternalError(e) from e raise SyncExternalError(e) from e
else: else:
# If no updates are found, Zabbix interface is in-sync # If no updates are found, Zabbix interface is in-sync
e = f"Device {self.name}: interface in-sync." e = f"Host {self.name}: interface in-sync."
self.logger.debug(e) self.logger.debug(e)
else: else:
e = (f"Device {self.name} has unsupported interface configuration." e = (f"Host {self.name} has unsupported interface configuration."
f" Host has total of {len(host['interfaces'])} interfaces. " f" Host has total of {len(host['interfaces'])} interfaces. "
"Manual interfention required.") "Manual interfention required.")
self.logger.error(e) self.logger.error(e)
@ -716,7 +722,7 @@ class NetworkDevice():
} }
try: try:
self.nb_journals.create(journal) self.nb_journals.create(journal)
self.logger.debug(f"Device {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 "
@ -743,7 +749,7 @@ class NetworkDevice():
# and add this NB template to the list of successfull templates # and add this NB template to the list of successfull templates
tmpls_from_zabbix.pop(pos) tmpls_from_zabbix.pop(pos)
succesfull_templates.append(nb_tmpl) succesfull_templates.append(nb_tmpl)
self.logger.debug(f"Device {self.name}: template " self.logger.debug(f"Host {self.name}: template "
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:

View File

@ -63,7 +63,9 @@ class Hostgroup():
format_options["manufacturer"] = self.nb.device_type.manufacturer.name format_options["manufacturer"] = self.nb.device_type.manufacturer.name
# Variables only applicable for VM's # Variables only applicable for VM's
if self.type == "vm": if self.type == "vm":
format_options["cluster"] = str(self.nb.cluster.name) if self.nb.cluster else None format_options["cluster"] = self.nb.cluster.name
format_options["cluster_type"] = self.nb.cluster.type.name
self.format_options = format_options self.format_options = format_options
def generate(self, hg_format=None): def generate(self, hg_format=None):

View File

@ -1,23 +1,41 @@
"""Module that hosts all functions for virtual machine processing""" """Module that hosts all functions for virtual machine processing"""
from modules.exceptions import * from modules.device import PhysicalDevice
from modules.hostgroups import Hostgroup
from modules.exceptions import TemplateError
try:
from config import (
traverse_site_groups,
traverse_regions,
template_cf
)
except ModuleNotFoundError:
print("Configuration file config.py not found in main directory."
"Please create the file or rename the config.py.example file to config.py.")
sys.exit(0)
class VirtualMachine(): class VirtualMachine(PhysicalDevice):
"""Model for virtual machines""" """Model for virtual machines"""
def __init__(self, nb, name): def set_hostgroup(self, hg_format, nb_site_groups, nb_regions):
self.nb = nb """Set the hostgroup for this device"""
self.name = name # Create new Hostgroup instance
self.hostgroup = None hg = Hostgroup("vm", self.nb, self.nb_api_version,
traverse_site_groups, traverse_regions,
nb_site_groups, nb_regions)
# Generate hostgroup based on hostgroup format
self.hostgroup = hg.generate(hg_format)
def __repr__(self): def set_vm_template(self):
return self.name """ Set Template for VMs. Overwrites default class
to skip a lookup of custom fields."""
self.zbx_template_names = None
# Gather templates ONLY from the device specific context
try:
self.zbx_template_names = self.get_templates_context()
except TemplateError as e:
self.logger.warning(e)
return True
def __str__(self): def set_template(self, **kwargs):
return self.__repr__() """Simple wrapper fur underlying functions"""
self.set_vm_template()
def _data_prep(self):
self.platform = self.nb.platform.name
self.cluster = self.nb.cluster.name
def set_hostgroup(self):
self.hostgroup = "Virtual machines"

View File

@ -7,7 +7,8 @@ import argparse
from os import environ, path, sys from os import environ, path, sys
from pynetbox import api from pynetbox import api
from zabbix_utils import ZabbixAPI, APIRequestError, ProcessingError from zabbix_utils import ZabbixAPI, APIRequestError, ProcessingError
from modules.device import NetworkDevice from modules.device import PhysicalDevice
from modules.virtualMachine import VirtualMachine
from modules.tools import convert_recordset, proxy_prepper from modules.tools import convert_recordset, proxy_prepper
from modules.exceptions import EnvironmentVarError, HostgroupError, SyncError from modules.exceptions import EnvironmentVarError, HostgroupError, SyncError
try: try:
@ -19,7 +20,9 @@ try:
zabbix_device_removal, zabbix_device_removal,
zabbix_device_disable, zabbix_device_disable,
hostgroup_format, hostgroup_format,
nb_device_filter vm_hostgroup_format,
nb_device_filter,
sync_vms
) )
except ModuleNotFoundError: except ModuleNotFoundError:
print("Configuration file config.py not found in main directory." print("Configuration file config.py not found in main directory."
@ -106,6 +109,7 @@ def main(arguments):
proxy_name = "name" proxy_name = "name"
# 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_vms = netbox.virtualization.virtual_machines.all() if sync_vms else list()
netbox_site_groups = convert_recordset((netbox.dcim.site_groups.all())) netbox_site_groups = convert_recordset((netbox.dcim.site_groups.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
@ -127,11 +131,53 @@ def main(arguments):
nb_version = netbox.version nb_version = netbox.version
# Go through all Netbox devices # Go through all Netbox devices
for nb_device in netbox_devices: try:
try: for nb_vm in netbox_vms:
vm = VirtualMachine(nb_vm, zabbix, netbox_journals, nb_version,
create_journal, logger)
vm.set_hostgroup(vm_hostgroup_format,netbox_site_groups,netbox_regions)
vm.set_vm_template()
vm.set_inventory(nb_vm)
print(f"Templates: {vm.zbx_template_names}")
# Checks if device is in cleanup state
if vm.status in zabbix_device_removal:
if vm.zabbix_id:
# Delete device from Zabbix
# and remove hostID from Netbox.
vm.cleanup()
logger.info(f"VM {vm.name}: cleanup complete")
continue
# Device has been added to Netbox
# but is not in Activate state
logger.info(f"VM {vm.name}: skipping since this VM is "
f"not in the active state.")
continue
# Check if the VM is in the disabled state
if vm.status in zabbix_device_disable:
vm.zabbix_state = 1
# Check if VM is already in Zabbix
if vm.zabbix_id:
vm.ConsistencyCheck(zabbix_groups, zabbix_templates,
zabbix_proxy_list, full_proxy_sync,
create_hostgroups)
continue
# Add hostgroup is config is set
if create_hostgroups:
# Create new hostgroup. Potentially multiple groups if nested
hostgroups = vm.createZabbixHostgroup(zabbix_groups)
# go through all newly created hostgroups
for group in hostgroups:
# Add new hostgroups to zabbix group list
zabbix_groups.append(group)
# Add VM to Zabbix
vm.createInZabbix(zabbix_groups, zabbix_templates,
zabbix_proxy_list)
for nb_device in netbox_devices:
# Set device instance set data such as hostgroup and template information. # Set device instance set data such as hostgroup and template information.
device = NetworkDevice(nb_device, zabbix, netbox_journals, nb_version, device = PhysicalDevice(nb_device, zabbix, netbox_journals, nb_version,
create_journal, logger) 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)
@ -183,8 +229,8 @@ 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)
except SyncError: except SyncError:
pass pass
if __name__ == "__main__": if __name__ == "__main__":