2
0

zabbix.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. import logging
  2. import requests
  3. import borgmatic.hooks.credential.parse
  4. logger = logging.getLogger(__name__)
  5. TIMEOUT_SECONDS = 10
  6. def initialize_monitor(
  7. ping_url,
  8. config,
  9. config_filename,
  10. monitoring_log_level,
  11. dry_run,
  12. ): # pragma: no cover
  13. '''
  14. No initialization is necessary for this monitor.
  15. '''
  16. def send_zabbix_request(server, headers, data):
  17. '''
  18. Given a Zabbix server URL, HTTP headers as a dict, and valid Zabbix JSON payload data as a dict,
  19. send a request to the Zabbix server via API.
  20. Return the response "result" value or None.
  21. '''
  22. logging.getLogger('urllib3').setLevel(logging.ERROR)
  23. logger.debug(f'Sending a "{data["method"]}" request to the Zabbix server')
  24. try:
  25. response = requests.post(server, headers=headers, json=data, timeout=TIMEOUT_SECONDS)
  26. if not response.ok:
  27. response.raise_for_status()
  28. except requests.exceptions.RequestException as error:
  29. logger.warning(f'Zabbix error: {error}')
  30. return None
  31. try:
  32. result = response.json().get('result')
  33. error_message = result['data'][0]['error']
  34. except requests.exceptions.JSONDecodeError:
  35. logger.warning('Zabbix error: Cannot parse API response')
  36. return None
  37. except (TypeError, KeyError, IndexError):
  38. return result
  39. else:
  40. logger.warning(f'Zabbix error: {error_message}')
  41. return None
  42. def ping_monitor(hook_config, config, config_filename, state, monitoring_log_level, dry_run): # noqa: PLR0911, PLR0912, PLR0915
  43. '''
  44. Update the configured Zabbix item using either the itemid, or a host and key.
  45. If this is a dry run, then don't actually update anything.
  46. '''
  47. run_states = hook_config.get('states', ['fail'])
  48. if state.name.lower() not in run_states:
  49. return
  50. dry_run_label = ' (dry run; not actually updating)' if dry_run else ''
  51. state_config = hook_config.get(
  52. state.name.lower(),
  53. {
  54. 'value': state.name.lower(),
  55. },
  56. )
  57. try:
  58. username = borgmatic.hooks.credential.parse.resolve_credential(
  59. hook_config.get('username'),
  60. config,
  61. )
  62. password = borgmatic.hooks.credential.parse.resolve_credential(
  63. hook_config.get('password'),
  64. config,
  65. )
  66. api_key = borgmatic.hooks.credential.parse.resolve_credential(
  67. hook_config.get('api_key'),
  68. config,
  69. )
  70. except ValueError as error:
  71. logger.warning(f'Zabbix credential error: {error}')
  72. return
  73. server = hook_config.get('server')
  74. itemid = hook_config.get('itemid')
  75. host = hook_config.get('host')
  76. key = hook_config.get('key')
  77. value = state_config.get('value')
  78. headers = {'Content-Type': 'application/json-rpc', 'User-Agent': 'borgmatic'}
  79. logger.info(f'Pinging Zabbix{dry_run_label}')
  80. logger.debug(f'Using Zabbix URL: {server}')
  81. # Determine the Zabbix method used to store the value: itemid or host/key
  82. if itemid is not None:
  83. logger.info(f'Updating {itemid} on Zabbix')
  84. data = {
  85. 'jsonrpc': '2.0',
  86. 'method': 'history.push',
  87. 'params': {'itemid': itemid, 'value': value},
  88. 'id': 1,
  89. }
  90. elif host is not None and key is not None:
  91. logger.info(f'Updating Host: "{host}" and Key: "{key}" on Zabbix')
  92. data = {
  93. 'jsonrpc': '2.0',
  94. 'method': 'history.push',
  95. 'params': {'host': host, 'key': key, 'value': value},
  96. 'id': 1,
  97. }
  98. elif host is not None:
  99. logger.warning('Key missing for Zabbix')
  100. return
  101. elif key is not None:
  102. logger.warning('Host missing for Zabbix')
  103. return
  104. else:
  105. logger.warning('No Zabbix itemid or host/key provided')
  106. return
  107. # Determine the authentication method: API key or username/password
  108. if api_key is not None:
  109. logger.info('Using API key auth for Zabbix')
  110. headers['Authorization'] = f'Bearer {api_key}'
  111. elif username is not None and password is not None:
  112. logger.info(f'Using user/pass auth with user {username} for Zabbix')
  113. login_data = {
  114. 'jsonrpc': '2.0',
  115. 'method': 'user.login',
  116. 'params': {'username': username, 'password': password},
  117. 'id': 1,
  118. }
  119. if not dry_run:
  120. result = send_zabbix_request(server, headers, login_data)
  121. if not result:
  122. return
  123. headers['Authorization'] = f'Bearer {result}'
  124. elif username is not None:
  125. logger.warning('Password missing for Zabbix authentication')
  126. return
  127. elif password is not None:
  128. logger.warning('Username missing for Zabbix authentication')
  129. return
  130. else:
  131. logger.warning('Authentication data missing for Zabbix')
  132. return
  133. if not dry_run:
  134. send_zabbix_request(server, headers, data)
  135. if username is not None and password is not None:
  136. logout_data = {
  137. 'jsonrpc': '2.0',
  138. 'method': 'user.logout',
  139. 'params': [],
  140. 'id': 1,
  141. }
  142. if not dry_run:
  143. send_zabbix_request(server, headers, logout_data)
  144. def destroy_monitor(ping_url_or_uuid, config, monitoring_log_level, dry_run): # pragma: no cover
  145. '''
  146. No destruction is necessary for this monitor.
  147. '''