Reworked checking Disk() attributes
* Added separate Disk().check_attributes() function * Can be used to check all KNOWN_ATTRIBUTES or just blocking ones * Renamed ATTRIBUTES to KNOWN_ATTRIBUTES for clarity * Renamed 'Critical' column to 'Blocking' * Added '(Failed)' note to attribute report * Addresses issue #131
This commit is contained in:
parent
07fdbcdd7c
commit
93102b5144
2 changed files with 61 additions and 51 deletions
|
|
@ -3,31 +3,25 @@
|
||||||
# vim: sts=2 sw=2 ts=2
|
# vim: sts=2 sw=2 ts=2
|
||||||
|
|
||||||
|
|
||||||
ATTRIBUTES = {
|
KNOWN_ATTRIBUTES = {
|
||||||
# NVMe
|
# NVMe
|
||||||
'critical_warning': {'Critical': True, 'Warning': None, 'Error': 1, 'Maximum': None, },
|
'critical_warning': {'Blocking': True, 'Warning': None, 'Error': 1, 'Maximum': None, },
|
||||||
'media_errors': {'Critical': False, 'Warning': None, 'Error': 1, 'Maximum': None, },
|
'media_errors': {'Blocking': False, 'Warning': None, 'Error': 1, 'Maximum': None, },
|
||||||
'power_on_hours': {'Critical': False, 'Warning': 17532, 'Error': 26298, 'Maximum': None, },
|
'power_on_hours': {'Blocking': False, 'Warning': 17532, 'Error': 26298, 'Maximum': None, },
|
||||||
'unsafe_shutdowns': {'Critical': False, 'Warning': 1, 'Error': None, 'Maximum': None, },
|
'unsafe_shutdowns': {'Blocking': False, 'Warning': 1, 'Error': None, 'Maximum': None, },
|
||||||
# SMART
|
# SMART
|
||||||
5: {'Hex': '05', 'Critical': True, 'Warning': None, 'Error': 1, 'Maximum': None, },
|
5: {'Hex': '05', 'Blocking': True, 'Warning': None, 'Error': 1, 'Maximum': None, },
|
||||||
9: {'Hex': '09', 'Critical': False, 'Warning': 17532, 'Error': 26298, 'Maximum': None, },
|
9: {'Hex': '09', 'Blocking': False, 'Warning': 17532, 'Error': 26298, 'Maximum': None, },
|
||||||
10: {'Hex': '10', 'Critical': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
|
10: {'Hex': '10', 'Blocking': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
|
||||||
184: {'Hex': 'B8', 'Critical': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
|
184: {'Hex': 'B8', 'Blocking': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
|
||||||
187: {'Hex': 'BB', 'Critical': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
|
187: {'Hex': 'BB', 'Blocking': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
|
||||||
188: {'Hex': 'BC', 'Critical': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
|
188: {'Hex': 'BC', 'Blocking': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
|
||||||
196: {'Hex': 'C4', 'Critical': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
|
196: {'Hex': 'C4', 'Blocking': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
|
||||||
197: {'Hex': 'C5', 'Critical': True, 'Warning': None, 'Error': 1, 'Maximum': None, },
|
197: {'Hex': 'C5', 'Blocking': True, 'Warning': None, 'Error': 1, 'Maximum': None, },
|
||||||
198: {'Hex': 'C6', 'Critical': True, 'Warning': None, 'Error': 1, 'Maximum': None, },
|
198: {'Hex': 'C6', 'Blocking': True, 'Warning': None, 'Error': 1, 'Maximum': None, },
|
||||||
199: {'Hex': 'C7', 'Critical': False, 'Warning': None, 'Error': 1, 'Maximum': None, },
|
199: {'Hex': 'C7', 'Blocking': False, 'Warning': None, 'Error': 1, 'Maximum': None, },
|
||||||
201: {'Hex': 'C9', 'Critical': False, 'Warning': None, 'Error': 1, 'Maximum': 10000, },
|
201: {'Hex': 'C9', 'Blocking': False, 'Warning': None, 'Error': 1, 'Maximum': 10000, },
|
||||||
}
|
}
|
||||||
ATTRIBUTE_COLORS = (
|
|
||||||
# NOTE: Ordered by ascending importance
|
|
||||||
('Warning', 'YELLOW'),
|
|
||||||
('Error', 'RED'),
|
|
||||||
('Maximum', 'PURPLE'),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,18 @@ import re
|
||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from wk.cfg.hw import ATTRIBUTES, ATTRIBUTE_COLORS
|
from wk.cfg.hw import KNOWN_ATTRIBUTES
|
||||||
from wk.exe import get_json_from_command, run_program
|
from wk.exe import get_json_from_command, run_program
|
||||||
from wk.std import bytes_to_string, color_string, string_to_bytes
|
from wk.std import bytes_to_string, color_string, string_to_bytes
|
||||||
|
|
||||||
|
|
||||||
# STATIC VARIABLES
|
# STATIC VARIABLES
|
||||||
|
ATTRIBUTE_COLORS = (
|
||||||
|
# NOTE: Ordered by ascending importance
|
||||||
|
('Warning', 'YELLOW'),
|
||||||
|
('Error', 'RED'),
|
||||||
|
('Maximum', 'PURPLE'),
|
||||||
|
)
|
||||||
KEY_NVME = 'nvme_smart_health_information_log'
|
KEY_NVME = 'nvme_smart_health_information_log'
|
||||||
KEY_SMART = 'ata_smart_attributes'
|
KEY_SMART = 'ata_smart_attributes'
|
||||||
KNOWN_RAM_VENDOR_IDS = {
|
KNOWN_RAM_VENDOR_IDS = {
|
||||||
|
|
@ -144,6 +150,36 @@ class Disk():
|
||||||
self.notes.append(note)
|
self.notes.append(note)
|
||||||
self.notes.sort()
|
self.notes.sort()
|
||||||
|
|
||||||
|
def check_attributes(self, only_blocking=False):
|
||||||
|
"""Check if any known attributes are failing, returns bool."""
|
||||||
|
attributes_ok = True
|
||||||
|
for attr, value in self.attributes.items():
|
||||||
|
# Skip unknown attributes
|
||||||
|
if attr not in KNOWN_ATTRIBUTES:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Get thresholds
|
||||||
|
blocking_attribute = KNOWN_ATTRIBUTES[attr].get('Blocking', False)
|
||||||
|
err_thresh = KNOWN_ATTRIBUTES[attr].get('Error', None)
|
||||||
|
max_thresh = KNOWN_ATTRIBUTES[attr].get('Maximum', None)
|
||||||
|
if not max_thresh:
|
||||||
|
max_thresh = float('inf')
|
||||||
|
|
||||||
|
# Skip non-blocking attributes if necessary
|
||||||
|
if only_blocking and not blocking_attribute:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Skip informational attributes
|
||||||
|
if not err_thresh:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Check attribute
|
||||||
|
if err_thresh <= value['raw'] < max_thresh:
|
||||||
|
attributes_ok = False
|
||||||
|
|
||||||
|
# Done
|
||||||
|
return attributes_ok
|
||||||
|
|
||||||
def enable_smart(self):
|
def enable_smart(self):
|
||||||
"""Try enabling SMART for this disk."""
|
"""Try enabling SMART for this disk."""
|
||||||
cmd = [
|
cmd = [
|
||||||
|
|
@ -163,7 +199,7 @@ class Disk():
|
||||||
value_color = 'GREEN'
|
value_color = 'GREEN'
|
||||||
|
|
||||||
# Skip attributes not in our list
|
# Skip attributes not in our list
|
||||||
if attr not in ATTRIBUTES:
|
if attr not in KNOWN_ATTRIBUTES:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# ID / Name
|
# ID / Name
|
||||||
|
|
@ -175,10 +211,12 @@ class Disk():
|
||||||
|
|
||||||
# Value color
|
# Value color
|
||||||
for threshold, color in ATTRIBUTE_COLORS:
|
for threshold, color in ATTRIBUTE_COLORS:
|
||||||
threshold_val = ATTRIBUTES.get(attr, {}).get(threshold, float('inf'))
|
threshold_val = KNOWN_ATTRIBUTES[attr].get(threshold, None)
|
||||||
if threshold_val and value['raw'] >= threshold_val:
|
if threshold_val and value['raw'] >= threshold_val:
|
||||||
value_color = color
|
value_color = color
|
||||||
if threshold == 'Maximum':
|
if threshold == 'Error':
|
||||||
|
note = '(Failed)'
|
||||||
|
elif threshold == 'Maximum':
|
||||||
note = '(invalid?)'
|
note = '(invalid?)'
|
||||||
|
|
||||||
# 199/C7 warning
|
# 199/C7 warning
|
||||||
|
|
@ -286,31 +324,9 @@ class Disk():
|
||||||
self.update_smart_details()
|
self.update_smart_details()
|
||||||
|
|
||||||
# Attributes
|
# Attributes
|
||||||
for attr, value in self.attributes.items():
|
if not self.check_attributes(only_blocking=True):
|
||||||
# Skip unknown attributes
|
blocking_event_encountered = True
|
||||||
if attr not in ATTRIBUTES:
|
LOG.error('%s: Blocked for failing attributes', self.path)
|
||||||
continue
|
|
||||||
|
|
||||||
# Get thresholds
|
|
||||||
critical = ATTRIBUTES[attr].get('Critical', False)
|
|
||||||
err_thresh = ATTRIBUTES[attr].get('Error', None)
|
|
||||||
max_thresh = ATTRIBUTES[attr].get('Maximum', None)
|
|
||||||
if not max_thresh:
|
|
||||||
max_thresh = float('inf')
|
|
||||||
|
|
||||||
# Skip non-critical attributes
|
|
||||||
if not critical:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Skip informational attributes
|
|
||||||
if not err_thresh:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Check attribute
|
|
||||||
if err_thresh <= value['raw'] < max_thresh:
|
|
||||||
blocking_event_encountered = True
|
|
||||||
msg = f'Failed attribute: {attr}'
|
|
||||||
LOG.error('%s %s', self.path, msg)
|
|
||||||
|
|
||||||
# NVMe status
|
# NVMe status
|
||||||
# TODO: See https://github.com/2Shirt/WizardKit/issues/130
|
# TODO: See https://github.com/2Shirt/WizardKit/issues/130
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue