Add Zabbix monitoring hook.

Merge pull request #85 from tony1661/zabbix-hook
This commit is contained in:
2024-10-29 09:01:15 -07:00
committed by GitHub
6 changed files with 599 additions and 0 deletions

View File

@@ -1609,6 +1609,86 @@ properties:
example:
- start
- finish
zabbix:
type: object
additionalProperties: false
properties:
itemid:
type: integer
description: |
The ID of the Zabbix item used for collecting data.
Unique across the entire Zabbix system.
example: 55105
host:
type: string
description: |
Host name where the item is stored. Required if "itemid" is not set.
example: borg-server
key:
type: string
description: |
Key of the host where the item is stored. Required if "itemid" is not set.
example: borg.status
server:
type: string
description: |
The address of your Zabbix instance.
example: https://zabbix.your-domain.com
username:
type: string
description: |
The username used for authentication. Not needed if using an API key.
example: testuser
password:
type: string
description: |
The password used for authentication. Not needed if using an API key.
example: fakepassword
api_key:
type: string
description: |
The API key used for authentication. Not needed if using an username/password.
example: fakekey
start:
type: object
properties:
value:
type: ["integer", "string"]
description: |
The value to set the item to on start.
example: STARTED
finish:
type: object
properties:
value:
type: ["integer", "string"]
description: |
The value to set the item to on finish.
example: FINISH
fail:
type: object
properties:
value:
type: ["integer", "string"]
description: |
The value to set the item to on fail.
example: ERROR
states:
type: array
items:
type: string
enum:
- start
- finish
- fail
uniqueItems: true
description: |
List of one or more monitoring states to ping for: "start",
"finish", and/or "fail". Defaults to pinging for failure
only.
example:
- start
- finish
apprise:
type: object
required: ['services']

View File

@@ -14,6 +14,7 @@ from borgmatic.hooks import (
postgresql,
sqlite,
uptimekuma,
zabbix,
)
logger = logging.getLogger(__name__)
@@ -32,6 +33,7 @@ HOOK_NAME_TO_MODULE = {
'postgresql_databases': postgresql,
'sqlite_databases': sqlite,
'uptime_kuma': uptimekuma,
'zabbix': zabbix,
}

View File

@@ -9,6 +9,7 @@ MONITOR_HOOK_NAMES = (
'ntfy',
'pagerduty',
'uptime_kuma',
'zabbix',
)

139
borgmatic/hooks/zabbix.py Normal file
View File

@@ -0,0 +1,139 @@
import json
import logging
import requests
logger = logging.getLogger(__name__)
def initialize_monitor(
ping_url, config, config_filename, monitoring_log_level, dry_run
): # pragma: no cover
'''
No initialization is necessary for this monitor.
'''
pass
def ping_monitor(hook_config, config, config_filename, state, monitoring_log_level, dry_run):
'''
Update the configured Zabbix item using either the itemid, or a host and key.
If this is a dry run, then don't actually update anything.
'''
run_states = hook_config.get('states', ['fail'])
if state.name.lower() not in run_states:
return
dry_run_label = ' (dry run; not actually updating)' if dry_run else ''
state_config = hook_config.get(
state.name.lower(),
{
'value': state.name.lower(),
},
)
server = hook_config.get('server')
username = hook_config.get('username')
password = hook_config.get('password')
api_key = hook_config.get('api_key')
itemid = hook_config.get('itemid')
host = hook_config.get('host')
key = hook_config.get('key')
value = state_config.get('value')
headers = {'Content-Type': 'application/json-rpc'}
logger.info(f'{config_filename}: Updating Zabbix{dry_run_label}')
logger.debug(f'{config_filename}: Using Zabbix URL: {server}')
if server is None:
logger.warning(f'{config_filename}: Server missing for Zabbix')
return
# Determine the zabbix method used to store the value: itemid or host/key
if itemid is not None:
logger.info(f'{config_filename}: Updating {itemid} on Zabbix')
data = {
"jsonrpc": "2.0",
"method": "history.push",
"params": {"itemid": itemid, "value": value},
"id": 1,
}
elif (host and key) is not None:
logger.info(f'{config_filename}: Updating Host:{host} and Key:{key} on Zabbix')
data = {
"jsonrpc": "2.0",
"method": "history.push",
"params": {"host": host, "key": key, "value": value},
"id": 1,
}
elif host is not None:
logger.warning(f'{config_filename}: Key missing for Zabbix')
return
elif key is not None:
logger.warning(f'{config_filename}: Host missing for Zabbix.')
return
else:
logger.warning(f'{config_filename}: No zabbix itemid or host/key provided.')
return
# Determine the authentication method: API key or username/password
if api_key is not None:
logger.info(f'{config_filename}: Using API key auth for Zabbix')
headers['Authorization'] = 'Bearer ' + api_key
elif (username and password) is not None:
logger.info(f'{config_filename}: Using user/pass auth with user {username} for Zabbix')
auth_data = {
"jsonrpc": "2.0",
"method": "user.login",
"params": {
"username": username,
"password": password
},
"id": 1
}
if not dry_run:
logging.getLogger('urllib3').setLevel(logging.ERROR)
try:
response = requests.post(server, headers=headers, json=auth_data)
data['auth'] = response.json().get('result')
if not response.ok:
response.raise_for_status()
except requests.exceptions.RequestException as error:
logger.warning(f'{config_filename}: Zabbix error: {error}')
elif username is not None:
logger.warning(f'{config_filename}: Password missing for Zabbix authentication')
return
elif password is not None:
logger.warning(f'{config_filename}: Username missing for Zabbix authentication')
return
else:
logger.warning(f'{config_filename}: Authentication data missing for Zabbix')
return
if not dry_run:
logging.getLogger('urllib3').setLevel(logging.ERROR)
try:
response = requests.post(server, headers=headers, json=data)
if not response.ok:
response.raise_for_status()
except requests.exceptions.RequestException as error:
logger.warning(f'{config_filename}: Zabbix error: {error}')
def destroy_monitor(
ping_url_or_uuid, config, config_filename, monitoring_log_level, dry_run
): # pragma: no cover
'''
No destruction is necessary for this monitor.
'''
pass