From 93102b5144d33a655ff9b0b25607131d2c5e027d Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Thu, 31 Oct 2019 19:19:52 -0600 Subject: [PATCH] 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 --- scripts/wk/cfg/hw.py | 38 ++++++++++------------- scripts/wk/hw/obj.py | 74 +++++++++++++++++++++++++++----------------- 2 files changed, 61 insertions(+), 51 deletions(-) diff --git a/scripts/wk/cfg/hw.py b/scripts/wk/cfg/hw.py index 06554c9e..27455bbd 100644 --- a/scripts/wk/cfg/hw.py +++ b/scripts/wk/cfg/hw.py @@ -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__': diff --git a/scripts/wk/hw/obj.py b/scripts/wk/hw/obj.py index bed9ec0a..8df8a459 100644 --- a/scripts/wk/hw/obj.py +++ b/scripts/wk/hw/obj.py @@ -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: - blocking_event_encountered = True - msg = f'Failed attribute: {attr}' - LOG.error('%s %s', self.path, msg) + if not self.check_attributes(only_blocking=True): + blocking_event_encountered = True + LOG.error('%s: Blocked for failing attributes', self.path) # NVMe status # TODO: See https://github.com/2Shirt/WizardKit/issues/130