diff options
Diffstat (limited to 'glances/plugins/smart/model.py')
-rw-r--r-- | glances/plugins/smart/model.py | 191 |
1 files changed, 0 insertions, 191 deletions
diff --git a/glances/plugins/smart/model.py b/glances/plugins/smart/model.py deleted file mode 100644 index 3cb5376b..00000000 --- a/glances/plugins/smart/model.py +++ /dev/null @@ -1,191 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of Glances. -# -# Copyright (C) 2018 Tim Nibert <docz2a@gmail.com> -# -# SPDX-License-Identifier: LGPL-3.0-only -# - -""" -Hard disk SMART attributes plugin. -Depends on pySMART and smartmontools -Must execute as root -"usermod -a -G disk USERNAME" is not sufficient unfortunately -SmartCTL (/usr/sbin/smartctl) must be in system path for python2. - -Regular PySMART is a python2 library. -We are using the pySMART.smartx updated library to support both python 2 and 3. - -If we only have disk group access (no root): -$ smartctl -i /dev/sda -smartctl 6.6 2016-05-31 r4324 [x86_64-linux-4.15.0-30-generic] (local build) -Copyright (C) 2002-16, Bruce Allen, Christian Franke, www.smartmontools.org - - -Probable ATA device behind a SAT layer -Try an additional '-d ata' or '-d sat' argument. - -This is not very hopeful: https://medium.com/opsops/why-smartctl-could-not-be-run-without-root-7ea0583b1323 - -So, here is what we are going to do: -Check for admin access. If no admin access, disable SMART plugin. - -If smartmontools is not installed, we should catch the error upstream in plugin initialization. -""" - -from glances.plugins.plugin.model import GlancesPluginModel -from glances.logger import logger -from glances.main import disable -from glances.globals import is_admin - -# Import plugin specific dependency -try: - from pySMART import DeviceList -except ImportError as e: - import_error_tag = True - logger.warning("Missing Python Lib ({}), HDD Smart plugin is disabled".format(e)) -else: - import_error_tag = False - - -def convert_attribute_to_dict(attr): - return { - 'name': attr.name, - 'num': attr.num, - 'flags': attr.flags, - 'raw': attr.raw, - 'value': attr.value, - 'worst': attr.worst, - 'threshold': attr.thresh, - 'type': attr.type, - 'updated': attr.updated, - 'when_failed': attr.when_failed, - } - - -def get_smart_data(): - """ - Get SMART attribute data - :return: list of multi leveled dictionaries - each dict has a key "DeviceName" with the identification of the device in smartctl - also has keys of the SMART attribute id, with value of another dict of the attributes - [ - { - "DeviceName": "/dev/sda blahblah", - "1": - { - "flags": "..", - "raw": "..", - etc, - } - ... - } - ] - """ - stats = [] - # get all devices - try: - devlist = DeviceList() - except TypeError as e: - # Catch error (see #1806) - logger.debug('Smart plugin error - Can not grab device list ({})'.format(e)) - global import_error_tag - import_error_tag = True - return stats - - for dev in devlist.devices: - stats.append( - { - 'DeviceName': '{} {}'.format(dev.name, dev.model), - } - ) - for attribute in dev.attributes: - if attribute is None: - pass - else: - attrib_dict = convert_attribute_to_dict(attribute) - - # we will use the attribute number as the key - num = attrib_dict.pop('num', None) - try: - assert num is not None - except Exception as e: - # we should never get here, but if we do, continue to next iteration and skip this attribute - logger.debug('Smart plugin error - Skip the attribute {} ({})'.format(attribute, e)) - continue - - stats[-1][num] = attrib_dict - return stats - - -class PluginModel(GlancesPluginModel): - """Glances' HDD SMART plugin.""" - - def __init__(self, args=None, config=None, stats_init_value=[]): - """Init the plugin.""" - # check if user is admin - if not is_admin(): - disable(args, "smart") - logger.debug("Current user is not admin, HDD SMART plugin disabled.") - - super(PluginModel, self).__init__(args=args, config=config) - - # We want to display the stat in the curse interface - self.display_curse = True - - @GlancesPluginModel._check_decorator - @GlancesPluginModel._log_result_decorator - def update(self): - """Update SMART stats using the input method.""" - # Init new stats - stats = self.get_init_value() - - if import_error_tag: - return self.stats - - if self.input_method == 'local': - stats = get_smart_data() - elif self.input_method == 'snmp': - pass - - # Update the stats - self.stats = stats - - return self.stats - - def get_key(self): - """Return the key of the list.""" - return 'DeviceName' - - def msg_curse(self, args=None, max_width=None): - """Return the dict to display in the curse interface.""" - # Init the return message - ret = [] - - # Only process if stats exist... - if import_error_tag or not self.stats or self.is_disabled(): - return ret - - # Max size for the interface name - name_max_width = max_width - 6 - - # Header - msg = '{:{width}}'.format('SMART disks', width=name_max_width) - ret.append(self.curse_add_line(msg, "TITLE")) - # Data - for device_stat in self.stats: - # New line - ret.append(self.curse_new_line()) - msg = '{:{width}}'.format(device_stat['DeviceName'][:max_width], width=max_width) - ret.append(self.curse_add_line(msg)) - for smart_stat in sorted([i for i in device_stat.keys() if i != 'DeviceName'], key=int): - ret.append(self.curse_new_line()) - msg = ' {:{width}}'.format( - device_stat[smart_stat]['name'][: name_max_width - 1].replace('_', ' '), width=name_max_width - 1 - ) - ret.append(self.curse_add_line(msg)) - msg = '{:>8}'.format(device_stat[smart_stat]['raw']) - ret.append(self.curse_add_line(msg)) - - return ret |