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:
2Shirt 2019-10-31 19:19:52 -06:00
parent 07fdbcdd7c
commit 93102b5144
Signed by: 2Shirt
GPG key ID: 152FAC923B0E132C
2 changed files with 61 additions and 51 deletions

View file

@ -3,31 +3,25 @@
# vim: sts=2 sw=2 ts=2
ATTRIBUTES = {
KNOWN_ATTRIBUTES = {
# NVMe
'critical_warning': {'Critical': True, 'Warning': None, 'Error': 1, 'Maximum': None, },
'media_errors': {'Critical': False, 'Warning': None, 'Error': 1, 'Maximum': None, },
'power_on_hours': {'Critical': False, 'Warning': 17532, 'Error': 26298, 'Maximum': None, },
'unsafe_shutdowns': {'Critical': False, 'Warning': 1, 'Error': None, 'Maximum': None, },
'critical_warning': {'Blocking': True, 'Warning': None, 'Error': 1, 'Maximum': None, },
'media_errors': {'Blocking': False, 'Warning': None, 'Error': 1, 'Maximum': None, },
'power_on_hours': {'Blocking': False, 'Warning': 17532, 'Error': 26298, 'Maximum': None, },
'unsafe_shutdowns': {'Blocking': False, 'Warning': 1, 'Error': None, 'Maximum': None, },
# SMART
5: {'Hex': '05', 'Critical': True, 'Warning': None, 'Error': 1, 'Maximum': None, },
9: {'Hex': '09', 'Critical': False, 'Warning': 17532, 'Error': 26298, 'Maximum': None, },
10: {'Hex': '10', 'Critical': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
184: {'Hex': 'B8', 'Critical': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
187: {'Hex': 'BB', 'Critical': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
188: {'Hex': 'BC', 'Critical': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
196: {'Hex': 'C4', 'Critical': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
197: {'Hex': 'C5', 'Critical': True, 'Warning': None, 'Error': 1, 'Maximum': None, },
198: {'Hex': 'C6', 'Critical': True, 'Warning': None, 'Error': 1, 'Maximum': None, },
199: {'Hex': 'C7', 'Critical': False, 'Warning': None, 'Error': 1, 'Maximum': None, },
201: {'Hex': 'C9', 'Critical': False, 'Warning': None, 'Error': 1, 'Maximum': 10000, },
5: {'Hex': '05', 'Blocking': True, 'Warning': None, 'Error': 1, 'Maximum': None, },
9: {'Hex': '09', 'Blocking': False, 'Warning': 17532, 'Error': 26298, 'Maximum': None, },
10: {'Hex': '10', 'Blocking': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
184: {'Hex': 'B8', 'Blocking': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
187: {'Hex': 'BB', 'Blocking': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
188: {'Hex': 'BC', 'Blocking': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
196: {'Hex': 'C4', 'Blocking': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
197: {'Hex': 'C5', 'Blocking': True, 'Warning': None, 'Error': 1, 'Maximum': None, },
198: {'Hex': 'C6', 'Blocking': True, 'Warning': None, 'Error': 1, 'Maximum': None, },
199: {'Hex': 'C7', 'Blocking': False, 'Warning': None, 'Error': 1, 'Maximum': None, },
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__':

View file

@ -9,12 +9,18 @@ import re
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.std import bytes_to_string, color_string, string_to_bytes
# STATIC VARIABLES
ATTRIBUTE_COLORS = (
# NOTE: Ordered by ascending importance
('Warning', 'YELLOW'),
('Error', 'RED'),
('Maximum', 'PURPLE'),
)
KEY_NVME = 'nvme_smart_health_information_log'
KEY_SMART = 'ata_smart_attributes'
KNOWN_RAM_VENDOR_IDS = {
@ -144,6 +150,36 @@ class Disk():
self.notes.append(note)
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):
"""Try enabling SMART for this disk."""
cmd = [
@ -163,7 +199,7 @@ class Disk():
value_color = 'GREEN'
# Skip attributes not in our list
if attr not in ATTRIBUTES:
if attr not in KNOWN_ATTRIBUTES:
continue
# ID / Name
@ -175,10 +211,12 @@ class Disk():
# Value color
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:
value_color = color
if threshold == 'Maximum':
if threshold == 'Error':
note = '(Failed)'
elif threshold == 'Maximum':
note = '(invalid?)'
# 199/C7 warning
@ -286,31 +324,9 @@ class Disk():
self.update_smart_details()
# Attributes
for attr, value in self.attributes.items():
# Skip unknown attributes
if attr not in ATTRIBUTES:
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:
if not self.check_attributes(only_blocking=True):
blocking_event_encountered = True
msg = f'Failed attribute: {attr}'
LOG.error('%s %s', self.path, msg)
LOG.error('%s: Blocked for failing attributes', self.path)
# NVMe status
# TODO: See https://github.com/2Shirt/WizardKit/issues/130