configure_zabbix_alerts.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. #ident "@(#)$Format:LocalFoodAI_lanfr144:configure_zabbix_alerts.py:%an:%ae:%ad:%cn:%ce:%cd:%H:%D:%N$"
  2. import json
  3. #ident "@(#)$Format:LocalFoodAI_lanfr144:configure_zabbix_alerts.py:%an:%ae:%ad:%cn:%ce:%cd:%H:%D:%N$"
  4. import urllib.request
  5. import os
  6. ZABBIX_URL = os.environ.get('ZABBIX_URL', 'http://192.168.130.170:8081/api_jsonrpc.php')
  7. ZABBIX_USER = os.environ.get('ZABBIX_USER', 'Admin')
  8. ZABBIX_PASS = os.environ.get('ZABBIX_PASS', '')
  9. DISCORD_WEBHOOK = os.environ.get('DISCORD_WEBHOOK', '')
  10. EMAIL_USER = os.environ.get('EMAIL_USER', 'lanfr144@gmail.com')
  11. EMAIL_PASS = os.environ.get('EMAIL_PASS', '')
  12. def zabbix_request(method, params, auth=None):
  13. payload = {
  14. 'jsonrpc': '2.0',
  15. 'method': method,
  16. 'params': params,
  17. 'id': 1
  18. }
  19. if auth:
  20. payload['auth'] = auth
  21. req = urllib.request.Request(ZABBIX_URL, data=json.dumps(payload).encode('utf-8'), headers={'Content-Type': 'application/json-rpc'})
  22. with urllib.request.urlopen(req) as response:
  23. res = json.loads(response.read().decode('utf-8'))
  24. if 'error' in res:
  25. print(f"Error in {method}: {res['error']}")
  26. return None
  27. return res['result']
  28. def main():
  29. # 1. Login
  30. auth_token = zabbix_request('user.login', {'username': ZABBIX_USER, 'password': ZABBIX_PASS})
  31. if not auth_token:
  32. print("Login failed")
  33. return
  34. print("Zabbix Auth Token:", auth_token)
  35. # 2. Configure Email Media Type
  36. # Find existing Email media type (type 0 = Email)
  37. email_mt = zabbix_request('mediatype.get', {'filter': {'type': '0'}}, auth_token)
  38. email_params = {
  39. 'type': '0',
  40. 'name': 'Email',
  41. 'smtp_server': 'smtp.gmail.com',
  42. 'smtp_port': '587',
  43. 'smtp_helo': 'gmail.com',
  44. 'smtp_email': EMAIL_USER,
  45. 'smtp_security': '1', # STARTTLS
  46. 'smtp_verify_peer': '0',
  47. 'smtp_verify_host': '0',
  48. 'smtp_authentication': '1',
  49. 'username': EMAIL_USER,
  50. 'passwd': EMAIL_PASS,
  51. 'content_type': '1' # HTML
  52. }
  53. if email_mt:
  54. email_params['mediatypeid'] = email_mt[0]['mediatypeid']
  55. zabbix_request('mediatype.update', email_params, auth_token)
  56. print("Updated Email Media Type")
  57. email_id = email_mt[0]['mediatypeid']
  58. else:
  59. res = zabbix_request('mediatype.create', email_params, auth_token)
  60. email_id = res['mediatypeids'][0]
  61. print("Created Email Media Type")
  62. # 3. Configure Discord Media Type (type 4 = Webhook)
  63. discord_script = """
  64. var req = new HttpRequest();
  65. req.addHeader('Content-Type: application/json');
  66. var webhook = params.URL;
  67. var payload = JSON.stringify({
  68. "content": params.Subject + "\\n" + params.Message
  69. });
  70. var resp = req.post(webhook, payload);
  71. if (req.getStatus() != 204 && req.getStatus() != 200) {
  72. throw 'Failed with status ' + req.getStatus();
  73. }
  74. return 'OK';
  75. """
  76. discord_mt = zabbix_request('mediatype.get', {'filter': {'name': 'Discord Webhook'}}, auth_token)
  77. discord_params = {
  78. 'name': 'Discord Webhook',
  79. 'type': '4',
  80. 'parameters': [
  81. {'name': 'URL', 'value': DISCORD_WEBHOOK},
  82. {'name': 'Subject', 'value': '{ALERT.SUBJECT}'},
  83. {'name': 'Message', 'value': '{ALERT.MESSAGE}'}
  84. ],
  85. 'script': discord_script,
  86. 'process_tags': '0'
  87. }
  88. if discord_mt:
  89. discord_params['mediatypeid'] = discord_mt[0]['mediatypeid']
  90. zabbix_request('mediatype.update', discord_params, auth_token)
  91. print("Updated Discord Media Type")
  92. discord_id = discord_mt[0]['mediatypeid']
  93. else:
  94. res = zabbix_request('mediatype.create', discord_params, auth_token)
  95. discord_id = res['mediatypeids'][0]
  96. print("Created Discord Media Type")
  97. # 4. Update Admin User Media
  98. users = zabbix_request('user.get', {'filter': {'username': 'Admin'}, 'selectMedias': 'extend'}, auth_token)
  99. if users:
  100. admin_id = users[0]['userid']
  101. medias = [
  102. {'mediatypeid': email_id, 'sendto': [EMAIL_USER], 'active': 0, 'severity': 63, 'period': '1-7,00:00-24:00'},
  103. {'mediatypeid': discord_id, 'sendto': ['Discord'], 'active': 0, 'severity': 63, 'period': '1-7,00:00-24:00'}
  104. ]
  105. zabbix_request('user.update', {'userid': admin_id, 'medias': medias}, auth_token)
  106. print("Updated Admin user media")
  107. # 5. Create Web Scenario & Trigger for "> 5 seconds"
  108. hosts = zabbix_request('host.get', {'filter': {'host': 'Zabbix server'}}, auth_token)
  109. if hosts:
  110. host_id = hosts[0]['hostid']
  111. # Create web scenario
  112. httptests = zabbix_request('httptest.get', {'filter': {'name': 'Food App Performance'}}, auth_token)
  113. if not httptests:
  114. zabbix_request('httptest.create', {
  115. 'name': 'Food App Performance',
  116. 'hostid': host_id,
  117. 'delay': '30s',
  118. 'steps': [
  119. {
  120. 'name': 'Homepage',
  121. 'url': 'http://172.18.0.3:8501', # Using docker internal IP or host IP
  122. 'status_codes': '200',
  123. 'no': 1
  124. }
  125. ]
  126. }, auth_token)
  127. print("Created HTTP Test for Food App")
  128. # Create Trigger > 5s
  129. triggers = zabbix_request('trigger.get', {'filter': {'description': 'Food App is too slow (> 5s)'}}, auth_token)
  130. if not triggers:
  131. zabbix_request('trigger.create', {
  132. 'description': 'Food App is too slow (> 5s)',
  133. 'expression': f'last(/Zabbix server/web.test.time[Food App Performance,Homepage,resp])>5',
  134. 'priority': 4 # High
  135. }, auth_token)
  136. print("Created Trigger for slow performance")
  137. triggers_down = zabbix_request('trigger.get', {'filter': {'description': 'Food App is DOWN'}}, auth_token)
  138. if not triggers_down:
  139. zabbix_request('trigger.create', {
  140. 'description': 'Food App is DOWN',
  141. 'expression': f'last(/Zabbix server/web.test.fail[Food App Performance])<>0',
  142. 'priority': 5 # Disaster
  143. }, auth_token)
  144. print("Created Trigger for downtime")
  145. # 6. Action to send message
  146. actions = zabbix_request('action.get', {'filter': {'name': 'Alert Discord and Email'}}, auth_token)
  147. if not actions:
  148. zabbix_request('action.create', {
  149. 'name': 'Alert Discord and Email',
  150. 'eventsource': 0,
  151. 'status': 0,
  152. 'esc_period': '1m',
  153. 'filter': {
  154. 'evaltype': 0,
  155. 'conditions': [
  156. {'conditiontype': 3, 'operator': 2, 'value': 'Food App'} # Trigger name contains "Food App"
  157. ]
  158. },
  159. 'operations': [
  160. {
  161. 'operationtype': 0,
  162. 'opmessage': {
  163. 'default_msg': 1,
  164. 'mediatypeid': 0 # all
  165. },
  166. 'opmessage_usr': [
  167. {'userid': admin_id}
  168. ]
  169. }
  170. ]
  171. }, auth_token)
  172. print("Created Action for Alerts")
  173. print("Zabbix Configuration Complete.")
  174. if __name__ == '__main__':
  175. main()