mirror of
https://github.com/FAUSheppy/icinga-webhook-gateway
synced 2025-12-05 23:11:43 +01:00
wip:
This commit is contained in:
27
server.py
27
server.py
@@ -24,6 +24,7 @@ from flask_sqlalchemy import SQLAlchemy
|
||||
from sqlalchemy.sql.expression import func
|
||||
|
||||
import icingatools
|
||||
import smarttools
|
||||
|
||||
app = flask.Flask("Icinga Report In Gateway")
|
||||
|
||||
@@ -72,6 +73,7 @@ class SMARTStatus(db.Model):
|
||||
model_number = Column(String)
|
||||
power_cycles = Column(Integer)
|
||||
power_on_hours = Column(Integer)
|
||||
wearleveling_count = Column(Integer)
|
||||
|
||||
def buildReponseDict(status, service=None):
|
||||
|
||||
@@ -311,6 +313,13 @@ def default():
|
||||
|
||||
smart = flask.request.json.get("smart")
|
||||
|
||||
# check smart json quoting problems #
|
||||
if smart and type(smart) == str:
|
||||
try:
|
||||
smart = json.loads(smart)
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
return ("Error in SMART-json {}".format(e), 415)
|
||||
|
||||
if not service:
|
||||
return ("'service' ist empty field in json", 400)
|
||||
elif not token:
|
||||
@@ -343,7 +352,10 @@ def default():
|
||||
|
||||
def record_and_check_smart(service, timestamp, smart):
|
||||
|
||||
health_info = smart["nvme_smart_health_information_log"]
|
||||
if "nvme_smart_health_information_log" in smart:
|
||||
health_info = smart["nvme_smart_health_information_log"]
|
||||
else:
|
||||
health_info = smarttools.normalize(smart)
|
||||
|
||||
if not service.special_type == "SMART":
|
||||
raise AssertionError("Trying to record SMART-record for non-SMART service")
|
||||
@@ -356,7 +368,8 @@ def record_and_check_smart(service, timestamp, smart):
|
||||
power_cycles=health_info["power_cycles"],
|
||||
power_on_hours=health_info["power_on_hours"],
|
||||
available_spare=health_info.get("available_spare"),
|
||||
model_number=smart.get("model_name"))
|
||||
model_number=smart.get("model_name"),
|
||||
wearleveling_count=health_info.get("wearleveling_count"))
|
||||
|
||||
db.session.add(smart_status)
|
||||
db.session.commit()
|
||||
@@ -378,6 +391,10 @@ def record_and_check_smart(service, timestamp, smart):
|
||||
if smart_last.critical_warning != 0:
|
||||
return ("SMART reports disk critical => oO better do something about this", "CRITICAL")
|
||||
|
||||
# wearleveling < 20% (SAMSUNG only) #
|
||||
if smart_last.wearleveling_count and smart_last.wearleveling_count <= 20:
|
||||
return ("SMART report prefail disk (wear_level < 20%)", "CRITICAL")
|
||||
|
||||
# temp max > X #
|
||||
if smart_last.temperature > 50:
|
||||
return ("Disk Temperatur {}".format(smart_last.temperature), "CRITICAL")
|
||||
@@ -386,7 +403,8 @@ def record_and_check_smart(service, timestamp, smart):
|
||||
spare_change = smart_old.available_spare - smart_last.available_spare
|
||||
|
||||
if smart_last.available_spare <= 25:
|
||||
return ("SSD spare <25 ({}) YOUR DISK WILL DIE SOON".format(spare_change), "CRITICAL")
|
||||
return ("SSD spare <25 ({}) YOUR DISK WILL DIE SOON".format(spare_change),
|
||||
"CRITICAL")
|
||||
elif smart_last.available_spare <= 50:
|
||||
return ("SSD spare <50 ({})".format(spare_change), "WARNING")
|
||||
elif spare_change >= 10:
|
||||
@@ -395,7 +413,8 @@ def record_and_check_smart(service, timestamp, smart):
|
||||
|
||||
# unsafe_shutdowns +1 #
|
||||
if smart_second_last.unsafe_shutdowns - smart_last.unsafe_shutdowns >= 1:
|
||||
return ("Disk had {} unsafe shutdowns".format(smart_last.unsafe_shutdowns), "WARNING")
|
||||
return ("Disk had {} unsafe shutdowns".format(smart_last.unsafe_shutdowns),
|
||||
"WARNING")
|
||||
|
||||
return ("", "OK")
|
||||
|
||||
|
||||
49
smarttools.py
Normal file
49
smarttools.py
Normal file
@@ -0,0 +1,49 @@
|
||||
def normalize(smart):
|
||||
'''Load different types of SMART outputs'''
|
||||
|
||||
ret = dict()
|
||||
ret.update({ "temperature" : 0 })
|
||||
ret.update({ "critical_warning" : 0 })
|
||||
ret.update({ "unsafe_shutdowns" : 0 })
|
||||
ret.update({ "power_cycles" : 0 })
|
||||
ret.update({ "power_on_hours" : 0 })
|
||||
ret.update({ "available_spare" : 100 })
|
||||
ret.update({ "wearleveling_count" : 100 })
|
||||
|
||||
if "ata_smart_attributes" in smart:
|
||||
|
||||
# get main table #
|
||||
table = smart["ata_smart_attributes"]["table"]
|
||||
|
||||
# temperatur #
|
||||
ret["temperature"] = smart["temperature"]["current"]
|
||||
|
||||
for el in table:
|
||||
|
||||
# look for relevant metrics #
|
||||
name = el["name"].lower()
|
||||
target_name = el["name"].lower() # name in return map
|
||||
|
||||
# handle value mapping #
|
||||
use_raw = False
|
||||
if name == "used_rsvd_blk_cnt_tot":
|
||||
target_name = "available_spare"
|
||||
elif name == "power_cylce_count":
|
||||
target_name = "power_cycles"
|
||||
use_raw = True
|
||||
elif name == "power_on_hours":
|
||||
target_name = "power_on_hours"
|
||||
use_raw = True
|
||||
|
||||
# check if metric should be recorded #
|
||||
if target_name in ret:
|
||||
|
||||
# set return dict #
|
||||
if use_raw:
|
||||
value = el["raw"]["value"]
|
||||
else:
|
||||
value = el["value"]
|
||||
|
||||
ret[target_name] = value
|
||||
|
||||
return ret
|
||||
@@ -115,6 +115,20 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if smart %}
|
||||
<h5 class="my-4">Windows</h5>
|
||||
<div class="ml-3 example">
|
||||
$SMART = @{ <br>
|
||||
<div class="example-indent">
|
||||
service = "{{ service.service }}"<br>
|
||||
token = "{{ service.token }}"<br>
|
||||
status = "N/A"<br>
|
||||
smart = "$(smartctl -a C: --json | Out-String)"<br>
|
||||
</div>
|
||||
} | ConvertTo-Json<br><br>
|
||||
Invoke-RestMethod -TimeoutSec 2 -Uri "{{ flask.request.url_root.replace("http://", "https://" )}}report" -Method Post -Headers @{"Content-Type"="application/json"} -Body $SMART
|
||||
</div>
|
||||
{% else %}
|
||||
<h5 class="my-4">Python</h5>
|
||||
<div class="ml-3 example">
|
||||
import requests<br>
|
||||
@@ -129,6 +143,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<table class="mb-4 mt-5 status-table">
|
||||
<thead>
|
||||
|
||||
Reference in New Issue
Block a user