Nagios Plugins
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

__init__.py 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. import json
  2. import logging
  3. import mqtt
  4. import nagios
  5. import time
  6. try:
  7. from config import APP_NAME as ROOT_LOGGER_NAME
  8. except ImportError:
  9. ROOT_LOGGER_NAME = 'root'
  10. logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
  11. class base(object):
  12. MONITORING_HEARTBEAT = "heartbeat"
  13. MONITORING_BATTERY = "battery"
  14. MONITORING_FOLLOW_SETPOINT = "follow_setpoint"
  15. MONITORING_LINKQUALITY = "linkquality"
  16. #
  17. FOLLOW_REQUEST_WARNING = 5 # Seconds, till warning comes up, if device does not follow the command
  18. FOLLOW_REQUEST_ERROR = 60 # Seconds, till error comes up, if device does not follow the command
  19. FOLLOW_KEY = None
  20. #
  21. BATTERY_LVL_WARNING = 15
  22. BATTERY_LVL_ERROR = 5
  23. #
  24. LINKQUALITY_WARNING = 50
  25. LINKQUALITY_ERROR = 25
  26. #
  27. LAST_MSG_WARNING = 6 * 60 * 60
  28. LAST_MSG_ERROR = 24 * 60 * 60
  29. def __init__(self, mqtt_client: mqtt.mqtt_client, topic):
  30. self.topic = topic
  31. #
  32. self.__unknown_tm__ = {}
  33. #
  34. mqtt_client.add_callback(topic, self.__rx__)
  35. mqtt_client.add_callback(topic + '/#', self.__rx__)
  36. #
  37. self.last_device_msg = None
  38. #
  39. self.__target_storage__ = {}
  40. self.__state_storage__ = {}
  41. #
  42. self.battery = None
  43. self.linkquality = None
  44. def __rx__(self, client, userdata, message):
  45. try:
  46. payload = json.loads(message.payload)
  47. except json.decoder.JSONDecodeError:
  48. pass
  49. else:
  50. if type(payload) is dict:
  51. #
  52. # heartbeat
  53. #
  54. if message.topic == self.topic:
  55. self.last_device_msg = time.time()
  56. #
  57. # follow setpoint
  58. #
  59. if self.FOLLOW_KEY is not None and self.FOLLOW_KEY in payload:
  60. if message.topic == self.topic + '/set':
  61. self.target(self.FOLLOW_KEY, payload[self.FOLLOW_KEY])
  62. if message.topic == self.topic:
  63. self.state(self.FOLLOW_KEY, payload[self.FOLLOW_KEY])
  64. #
  65. # battery level
  66. #
  67. if self.MONITORING_BATTERY in payload and message.topic == self.topic:
  68. self.battery = payload[self.MONITORING_BATTERY]
  69. #
  70. # linkquality
  71. #
  72. if self.MONITORING_LINKQUALITY in payload and message.topic == self.topic:
  73. self.linkquality = payload[self.MONITORING_LINKQUALITY]
  74. def target(self, key, value):
  75. tm_t, value_t = self.__target_storage__.get(key, (0, None))
  76. if value != value_t:
  77. self.__target_storage__[key] = time.time(), value
  78. logger.debug("Target value for device identified: %s: %s", key, repr(value))
  79. def state(self, key, value):
  80. self.__state_storage__[key] = time.time(), value
  81. logger.debug("Device state identified: %s: %s", key, repr(value))
  82. def status(self, key):
  83. #
  84. # HEARTBEAT
  85. #
  86. if key == self.MONITORING_HEARTBEAT:
  87. if self.last_device_msg is None:
  88. return self.__nagios_return__(self.MONITORING_HEARTBEAT, nagios.Nagios.UNKNOWN, "Device exists, but no data received")
  89. else:
  90. dt = time.time() - self.last_device_msg
  91. dt_disp = dt / 60 / 60
  92. if dt > self.LAST_MSG_ERROR:
  93. return self.__nagios_return__(self.MONITORING_HEARTBEAT, nagios.Nagios.ERROR, "Last message %.1fh ago" % dt_disp)
  94. elif dt > self.LAST_MSG_WARNING:
  95. return self.__nagios_return__(self.MONITORING_HEARTBEAT, nagios.Nagios.WARNING, "Last message %.1fh ago" % dt_disp)
  96. else:
  97. return self.__nagios_return__(self.MONITORING_HEARTBEAT, nagios.Nagios.OK, "Last message %.1fh ago" % dt_disp)
  98. #
  99. # FOLLOW SETPOINT
  100. #
  101. elif key == self.MONITORING_FOLLOW_SETPOINT:
  102. if self.FOLLOW_KEY is None:
  103. return self.__nagios_return__(self.MONITORING_FOLLOW_SETPOINT, nagios.Nagios.UNKNOWN, "Device exist, but does not follow any setpoint.", force=True)
  104. tm_s, value_s = self.__state_storage__.get(self.FOLLOW_KEY, (0, None))
  105. try:
  106. tm_t, value_t = self.__target_storage__[self.FOLLOW_KEY]
  107. except KeyError:
  108. if value_s is not None:
  109. return self.__nagios_return__(self.MONITORING_FOLLOW_SETPOINT, nagios.Nagios.OK, "Current temperature setpoint %.1f°C, but never received a setpoint. That might be okay." % value_s)
  110. return self.__nagios_return__(self.MONITORING_FOLLOW_SETPOINT, nagios.Nagios.UNKNOWN, "Device exists, but no data received")
  111. else:
  112. tm = time.time()
  113. dt = tm - tm_t
  114. if value_t != value_s and dt > self.FOLLOW_REQUEST_ERROR:
  115. return self.__nagios_return__(self.MONITORING_FOLLOW_SETPOINT, nagios.Nagios.ERROR, "Requested setpoint %.1f°C unequal valve setpoint %.1f°C since %.1fmin" % (value_t, value_s, (time.time()-tm_s)/60))
  116. elif value_t != value_s and dt > self.FOLLOW_REQUEST_WARNING:
  117. return self.__nagios_return__(self.MONITORING_FOLLOW_SETPOINT, nagios.Nagios.WARNING, "Requested setpoint %.1f°C unequal valve setpoint %.1f°C since %.1fmin" % (value_t, value_s, (time.time()-tm_s)))
  118. return self.__nagios_return__(self.MONITORING_FOLLOW_SETPOINT, nagios.Nagios.OK, "Requested setpoint equal valve setpoint %.1f°C" % value_s)
  119. #
  120. # BATTERY
  121. #
  122. elif key == self.MONITORING_BATTERY:
  123. if self.battery is None:
  124. return self.__nagios_return__(self.MONITORING_BATTERY, nagios.Nagios.UNKNOWN, "Device exists, but no data received or unknown monitoring")
  125. elif self.battery <= self.BATTERY_LVL_ERROR:
  126. return self.__nagios_return__(self.MONITORING_BATTERY, nagios.Nagios.ERROR, "Battery level critical low (%.1f%%)" % self.battery)
  127. elif self.battery <= self.BATTERY_LVL_WARNING:
  128. return self.__nagios_return__(self.MONITORING_BATTERY, nagios.Nagios.WARNING, "Battery level low (%.1f%%)" % self.battery)
  129. else:
  130. return self.__nagios_return__(self.MONITORING_BATTERY, nagios.Nagios.OK, "Battery okay (%.1f%%)" % self.battery)
  131. #
  132. # LINKQUALITY
  133. #
  134. elif key == self.MONITORING_LINKQUALITY:
  135. if self.linkquality is None:
  136. return self.__nagios_return__(self.MONITORING_LINKQUALITY, nagios.Nagios.UNKNOWN, "Device exists, but no data received or unknown monitoring")
  137. elif self.linkquality <= self.LINKQUALITY_ERROR:
  138. return self.__nagios_return__(self.MONITORING_LINKQUALITY, nagios.Nagios.ERROR, "Linkquality critical low (%d)" % self.linkquality)
  139. elif self.linkquality <= self.LINKQUALITY_WARNING:
  140. return self.__nagios_return__(self.MONITORING_LINKQUALITY, nagios.Nagios.WARNING, "Linkquality level low (%d)" % self.linkquality)
  141. else:
  142. return self.__nagios_return__(self.MONITORING_LINKQUALITY, nagios.Nagios.OK, "Linkquality okay (%d)" % self.linkquality)
  143. def __nagios_return__(self, monitoring_name, status, msg, force=False):
  144. tm = time.time()
  145. if monitoring_name not in self.__unknown_tm__:
  146. self.__unknown_tm__[monitoring_name] = None
  147. if status == nagios.Nagios.UNKNOWN and not force:
  148. if self.__unknown_tm__[monitoring_name] is None:
  149. self.__unknown_tm__[monitoring_name] = tm
  150. dt = tm - self.__unknown_tm__[monitoring_name]
  151. if dt >= self.LAST_MSG_ERROR:
  152. status = nagios.Nagios.UNKNOWN
  153. elif dt >= self.LAST_MSG_WARNING:
  154. status = nagios.Nagios.WARNING
  155. else:
  156. status = nagios.Nagios.OK
  157. msg += " - since %.1fh" % (dt / 3600)
  158. else:
  159. self.__unknown_tm__[monitoring_name] = None
  160. return {"status": status, "msg": msg}
  161. class group(object):
  162. def __init__(self, *args, **kwargs):
  163. pass
  164. class shelly_sw1(base):
  165. pass
  166. class tradfri_sw(base):
  167. pass
  168. class tradfri_sw_br(base):
  169. pass
  170. class tradfri_sw_br_ct(base):
  171. pass
  172. class tradfri_button(base):
  173. LAST_MSG_WARNING = 24 * 60 * 60
  174. LAST_MSG_ERROR = 48 * 60 * 60
  175. class livarno_sw_br_ct(base):
  176. pass
  177. class brennenstuhl_heatingvalve(base):
  178. BATTERY_LVL_WARNING = 4
  179. BATTERY_LVL_ERROR = 3
  180. FOLLOW_KEY = "current_heating_setpoint"
  181. class silvercrest_powerplug(base):
  182. pass
  183. class silvercrest_motion_sensor(base):
  184. pass
  185. class my_powerplug(base):
  186. pass
  187. class audio_status(base):
  188. pass
  189. class remote(base):
  190. pass