Added Disk() obj

This commit is contained in:
2Shirt 2019-10-23 15:23:50 -07:00
parent 6e557da370
commit 52d61226a0
Signed by: 2Shirt
GPG key ID: 152FAC923B0E132C
2 changed files with 129 additions and 1 deletions

View file

@ -97,7 +97,7 @@ def get_json_from_command(cmd, check=True, encoding='utf-8', errors='ignore'):
"""Capture JSON content from cmd output, returns dict. """Capture JSON content from cmd output, returns dict.
If the data can't be decoded then either an exception is raised If the data can't be decoded then either an exception is raised
or an empty dict is returned depending on ignore_errors. or an empty dict is returned depending on errors.
""" """
json_data = {} json_data = {}

View file

@ -1,13 +1,23 @@
"""WizardKit: Objects.""" """WizardKit: Objects."""
# vim: sts=2 sw=2 ts=2 # vim: sts=2 sw=2 ts=2
import logging
import pathlib import pathlib
import re
from collections import OrderedDict from collections import OrderedDict
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
KEY_NVME = 'nvme_smart_health_information_log'
KEY_SMART = 'ata_smart_attributes'
LOG = logging.getLogger(__name__)
REGEX_POWER_ON_TIME = re.compile(
r'^(\d+)([Hh].*|\s+\(\d+\s+\d+\s+\d+\).*)'
)
# Classes # Classes
class CpuRam(): class CpuRam():
"""Object for tracking CPU & RAM specific data.""" """Object for tracking CPU & RAM specific data."""
@ -87,5 +97,123 @@ class CpuRam():
return report return report
class Disk():
"""Object for tracking disk specific data."""
def __init__(self, path):
self.attributes = {}
self.description = 'UNKNOWN'
self.lsblk = {}
self.nvme_smart_notes = {}
self.path = pathlib.Path(path).resolve()
self.smartctl = {}
self.tests = OrderedDict()
# Update details
self.get_details()
self.enable_smart()
self.update_smart_details()
def enable_smart(self):
"""Try enabling SMART for this disk."""
cmd = [
'sudo',
'smartctl',
'--tolerance=permissive',
'--smart=on',
self.path,
]
run_program(cmd, check=False)
def get_details(self):
"""Get details via lsblk.
Required details default to generic descriptions and
are converted to the correct type.
"""
cmd = ['lsblk', '--bytes', '--json', '--output-all', '--paths', self.path]
json_data = get_json_from_command(cmd)
self.lsblk = json_data.get('blockdevices', [{}])[0]
# Set necessary details
self.lsblk['model'] = self.lsblk.get('model', 'Unknown Model')
self.lsblk['name'] = self.lsblk.get('name', self.path)
self.lsblk['log-sec'] = self.lsblk.get('log-sec', 512)
self.lsblk['phy-sec'] = self.lsblk.get('phy-sec', 512)
self.lsblk['rota'] = self.lsblk.get('rota', True)
self.lsblk['serial'] = self.lsblk.get('serial', 'Unknown Serial')
self.lsblk['size'] = self.lsblk.get('size', -1)
self.lsblk['tran'] = self.lsblk.get('tran', '???')
self.lsblk['tran'] = self.lsblk['tran'].upper().replace('NVME', 'NVMe')
# Ensure certain attributes types
for attr in ['model', 'name', 'serial', 'tran']:
if not isinstance(self.lsblk[attr], str):
self.lsblk[attr] = str(self.lsblk[attr])
for attr in ['log-sec', 'phy-sec', 'size']:
if not isinstance(self.lsblk[attr], int):
self.lsblk[attr] = int(self.lsblk[attr])
def get_labels(self):
"""Build list of labels for this disk, returns list."""
labels = []
# Add all labels from lsblk
for disk in [self.lsblk, *self.lsblk.get('children', [])]:
labels.append(disk.get('label', ''))
labels.append(disk.get('partlabel', ''))
# Remove empty labels
labels = [str(label) for label in labels if label]
# Done
return labels
def update_smart_details(self):
"""Update SMART details via smartctl."""
self.attributes = {}
cmd = [
'sudo',
'smartctl',
'--tolerance=verypermissive',
'--all',
'--json',
self.path,
]
self.smartctl = get_json_from_command(cmd)
# Check for attributes
if KEY_NVME in self.smartctl:
for name, value in self.smartctl[KEY_NVME].items():
try:
self.attributes[name] = {
'name': name,
'raw': int(value),
'raw_str': str(value),
}
except ValueError:
# Ignoring invalid attribute
LOG.error('Invalid NVMe attribute: %s %s', name, value)
elif KEY_SMART in self.smartctl:
for attribute in self.smartctl[KEY_SMART].get('table', {}):
try:
_id = int(attribute['id'])
except (KeyError, ValueError):
# Ignoring invalid attribute
LOG.error('Invalid SMART attribute: %s', attribute)
continue
name = str(attribute.get('name', 'UNKNOWN')).replace('_', ' ').title()
raw = int(attribute.get('raw', {}).get('value', -1))
raw_str = attribute.get('raw', {}).get('string', 'UNKNOWN')
# Fix power-on time
match = REGEX_POWER_ON_TIME.match(raw_str)
if _id == 9 and match:
raw = int(match.group(1))
# Add to dict
self.attributes[_id] = {
'name': name, 'raw': raw, 'raw_str': raw_str}
if __name__ == '__main__': if __name__ == '__main__':
print("This file is not meant to be called directly.") print("This file is not meant to be called directly.")