123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768 |
- # -*- coding: utf-8 -*-
- # Description: smart netdata python.d module
- # Author: ilyam8, vorph1
- # SPDX-License-Identifier: GPL-3.0-or-later
- import os
- import re
- from copy import deepcopy
- from time import time
- from bases.FrameworkServices.SimpleService import SimpleService
- from bases.collection import read_last_line
- INCREMENTAL = 'incremental'
- ABSOLUTE = 'absolute'
- ATA = 'ata'
- SCSI = 'scsi'
- CSV = '.csv'
- DEF_RESCAN_INTERVAL = 60
- DEF_AGE = 30
- DEF_PATH = '/var/log/smartd'
- ATTR1 = '1'
- ATTR2 = '2'
- ATTR3 = '3'
- ATTR4 = '4'
- ATTR5 = '5'
- ATTR7 = '7'
- ATTR8 = '8'
- ATTR9 = '9'
- ATTR10 = '10'
- ATTR11 = '11'
- ATTR12 = '12'
- ATTR13 = '13'
- ATTR170 = '170'
- ATTR171 = '171'
- ATTR172 = '172'
- ATTR173 = '173'
- ATTR174 = '174'
- ATTR180 = '180'
- ATTR183 = '183'
- ATTR190 = '190'
- ATTR194 = '194'
- ATTR196 = '196'
- ATTR197 = '197'
- ATTR198 = '198'
- ATTR199 = '199'
- ATTR202 = '202'
- ATTR206 = '206'
- ATTR233 = '233'
- ATTR249 = '249'
- ATTR_READ_ERR_COR = 'read-total-err-corrected'
- ATTR_READ_ERR_UNC = 'read-total-unc-errors'
- ATTR_WRITE_ERR_COR = 'write-total-err-corrected'
- ATTR_WRITE_ERR_UNC = 'write-total-unc-errors'
- ATTR_VERIFY_ERR_COR = 'verify-total-err-corrected'
- ATTR_VERIFY_ERR_UNC = 'verify-total-unc-errors'
- ATTR_TEMPERATURE = 'temperature'
- RE_ATA = re.compile(
- '(\d+);' # attribute
- '(\d+);' # normalized value
- '(\d+)', # raw value
- re.X
- )
- RE_SCSI = re.compile(
- '([a-z-]+);' # attribute
- '([0-9.]+)', # raw value
- re.X
- )
- ORDER = [
- # errors
- 'read_error_rate',
- 'seek_error_rate',
- 'soft_read_error_rate',
- 'write_error_rate',
- 'read_total_err_corrected',
- 'read_total_unc_errors',
- 'write_total_err_corrected',
- 'write_total_unc_errors',
- 'verify_total_err_corrected',
- 'verify_total_unc_errors',
- # external failure
- 'sata_interface_downshift',
- 'udma_crc_error_count',
- # performance
- 'throughput_performance',
- 'seek_time_performance',
- # power
- 'start_stop_count',
- 'power_on_hours_count',
- 'power_cycle_count',
- 'unexpected_power_loss',
- # spin
- 'spin_up_time',
- 'spin_up_retries',
- 'calibration_retries',
- # temperature
- 'airflow_temperature_celsius',
- 'temperature_celsius',
- # wear
- 'reallocated_sectors_count',
- 'reserved_block_count',
- 'program_fail_count',
- 'erase_fail_count',
- 'wear_leveller_worst_case_erase_count',
- 'unused_reserved_nand_blocks',
- 'reallocation_event_count',
- 'current_pending_sector_count',
- 'offline_uncorrectable_sector_count',
- 'percent_lifetime_used',
- 'media_wearout_indicator',
- ]
- CHARTS = {
- 'read_error_rate': {
- 'options': [None, 'Read Error Rate', 'value', 'errors', 'smartd_log.read_error_rate', 'line'],
- 'lines': [],
- 'attrs': [ATTR1],
- 'algo': ABSOLUTE,
- },
- 'seek_error_rate': {
- 'options': [None, 'Seek Error Rate', 'value', 'errors', 'smartd_log.seek_error_rate', 'line'],
- 'lines': [],
- 'attrs': [ATTR7],
- 'algo': ABSOLUTE,
- },
- 'soft_read_error_rate': {
- 'options': [None, 'Soft Read Error Rate', 'errors', 'errors', 'smartd_log.soft_read_error_rate', 'line'],
- 'lines': [],
- 'attrs': [ATTR13],
- 'algo': INCREMENTAL,
- },
- 'write_error_rate': {
- 'options': [None, 'Write Error Rate', 'value', 'errors', 'smartd_log.write_error_rate', 'line'],
- 'lines': [],
- 'attrs': [ATTR206],
- 'algo': ABSOLUTE,
- },
- 'read_total_err_corrected': {
- 'options': [None, 'Read Error Corrected', 'errors', 'errors', 'smartd_log.read_total_err_corrected', 'line'],
- 'lines': [],
- 'attrs': [ATTR_READ_ERR_COR],
- 'algo': INCREMENTAL,
- },
- 'read_total_unc_errors': {
- 'options': [None, 'Read Error Uncorrected', 'errors', 'errors', 'smartd_log.read_total_unc_errors', 'line'],
- 'lines': [],
- 'attrs': [ATTR_READ_ERR_UNC],
- 'algo': INCREMENTAL,
- },
- 'write_total_err_corrected': {
- 'options': [None, 'Write Error Corrected', 'errors', 'errors', 'smartd_log.write_total_err_corrected', 'line'],
- 'lines': [],
- 'attrs': [ATTR_WRITE_ERR_COR],
- 'algo': INCREMENTAL,
- },
- 'write_total_unc_errors': {
- 'options': [None, 'Write Error Uncorrected', 'errors', 'errors', 'smartd_log.write_total_unc_errors', 'line'],
- 'lines': [],
- 'attrs': [ATTR_WRITE_ERR_UNC],
- 'algo': INCREMENTAL,
- },
- 'verify_total_err_corrected': {
- 'options': [None, 'Verify Error Corrected', 'errors', 'errors', 'smartd_log.verify_total_err_corrected',
- 'line'],
- 'lines': [],
- 'attrs': [ATTR_VERIFY_ERR_COR],
- 'algo': INCREMENTAL,
- },
- 'verify_total_unc_errors': {
- 'options': [None, 'Verify Error Uncorrected', 'errors', 'errors', 'smartd_log.verify_total_unc_errors', 'line'],
- 'lines': [],
- 'attrs': [ATTR_VERIFY_ERR_UNC],
- 'algo': INCREMENTAL,
- },
- 'sata_interface_downshift': {
- 'options': [None, 'SATA Interface Downshift', 'events', 'external failure',
- 'smartd_log.sata_interface_downshift', 'line'],
- 'lines': [],
- 'attrs': [ATTR183],
- 'algo': INCREMENTAL,
- },
- 'udma_crc_error_count': {
- 'options': [None, 'UDMA CRC Error Count', 'errors', 'external failure', 'smartd_log.udma_crc_error_count',
- 'line'],
- 'lines': [],
- 'attrs': [ATTR199],
- 'algo': INCREMENTAL,
- },
- 'throughput_performance': {
- 'options': [None, 'Throughput Performance', 'value', 'performance', 'smartd_log.throughput_performance',
- 'line'],
- 'lines': [],
- 'attrs': [ATTR2],
- 'algo': ABSOLUTE,
- },
- 'seek_time_performance': {
- 'options': [None, 'Seek Time Performance', 'value', 'performance', 'smartd_log.seek_time_performance', 'line'],
- 'lines': [],
- 'attrs': [ATTR8],
- 'algo': ABSOLUTE,
- },
- 'start_stop_count': {
- 'options': [None, 'Start/Stop Count', 'events', 'power', 'smartd_log.start_stop_count', 'line'],
- 'lines': [],
- 'attrs': [ATTR4],
- 'algo': ABSOLUTE,
- },
- 'power_on_hours_count': {
- 'options': [None, 'Power-On Hours Count', 'hours', 'power', 'smartd_log.power_on_hours_count', 'line'],
- 'lines': [],
- 'attrs': [ATTR9],
- 'algo': ABSOLUTE,
- },
- 'power_cycle_count': {
- 'options': [None, 'Power Cycle Count', 'events', 'power', 'smartd_log.power_cycle_count', 'line'],
- 'lines': [],
- 'attrs': [ATTR12],
- 'algo': ABSOLUTE,
- },
- 'unexpected_power_loss': {
- 'options': [None, 'Unexpected Power Loss', 'events', 'power', 'smartd_log.unexpected_power_loss', 'line'],
- 'lines': [],
- 'attrs': [ATTR174],
- 'algo': ABSOLUTE,
- },
- 'spin_up_time': {
- 'options': [None, 'Spin-Up Time', 'ms', 'spin', 'smartd_log.spin_up_time', 'line'],
- 'lines': [],
- 'attrs': [ATTR3],
- 'algo': ABSOLUTE,
- },
- 'spin_up_retries': {
- 'options': [None, 'Spin-up Retries', 'retries', 'spin', 'smartd_log.spin_up_retries', 'line'],
- 'lines': [],
- 'attrs': [ATTR10],
- 'algo': INCREMENTAL,
- },
- 'calibration_retries': {
- 'options': [None, 'Calibration Retries', 'retries', 'spin', 'smartd_log.calibration_retries', 'line'],
- 'lines': [],
- 'attrs': [ATTR11],
- 'algo': INCREMENTAL,
- },
- 'airflow_temperature_celsius': {
- 'options': [None, 'Airflow Temperature Celsius', 'celsius', 'temperature',
- 'smartd_log.airflow_temperature_celsius', 'line'],
- 'lines': [],
- 'attrs': [ATTR190],
- 'algo': ABSOLUTE,
- },
- 'temperature_celsius': {
- 'options': [None, 'Temperature', 'celsius', 'temperature', 'smartd_log.temperature_celsius', 'line'],
- 'lines': [],
- 'attrs': [ATTR194, ATTR_TEMPERATURE],
- 'algo': ABSOLUTE,
- },
- 'reallocated_sectors_count': {
- 'options': [None, 'Reallocated Sectors Count', 'sectors', 'wear', 'smartd_log.reallocated_sectors_count',
- 'line'],
- 'lines': [],
- 'attrs': [ATTR5],
- 'algo': ABSOLUTE,
- },
- 'reserved_block_count': {
- 'options': [None, 'Reserved Block Count', 'percentage', 'wear', 'smartd_log.reserved_block_count', 'line'],
- 'lines': [],
- 'attrs': [ATTR170],
- 'algo': ABSOLUTE,
- },
- 'program_fail_count': {
- 'options': [None, 'Program Fail Count', 'errors', 'wear', 'smartd_log.program_fail_count', 'line'],
- 'lines': [],
- 'attrs': [ATTR171],
- 'algo': INCREMENTAL,
- },
- 'erase_fail_count': {
- 'options': [None, 'Erase Fail Count', 'failures', 'wear', 'smartd_log.erase_fail_count', 'line'],
- 'lines': [],
- 'attrs': [ATTR172],
- 'algo': INCREMENTAL,
- },
- 'wear_leveller_worst_case_erase_count': {
- 'options': [None, 'Wear Leveller Worst Case Erase Count', 'erases', 'wear',
- 'smartd_log.wear_leveller_worst_case_erase_count', 'line'],
- 'lines': [],
- 'attrs': [ATTR173],
- 'algo': ABSOLUTE,
- },
- 'unused_reserved_nand_blocks': {
- 'options': [None, 'Unused Reserved NAND Blocks', 'blocks', 'wear', 'smartd_log.unused_reserved_nand_blocks',
- 'line'],
- 'lines': [],
- 'attrs': [ATTR180],
- 'algo': ABSOLUTE,
- },
- 'reallocation_event_count': {
- 'options': [None, 'Reallocation Event Count', 'events', 'wear', 'smartd_log.reallocation_event_count', 'line'],
- 'lines': [],
- 'attrs': [ATTR196],
- 'algo': INCREMENTAL,
- },
- 'current_pending_sector_count': {
- 'options': [None, 'Current Pending Sector Count', 'sectors', 'wear', 'smartd_log.current_pending_sector_count',
- 'line'],
- 'lines': [],
- 'attrs': [ATTR197],
- 'algo': ABSOLUTE,
- },
- 'offline_uncorrectable_sector_count': {
- 'options': [None, 'Offline Uncorrectable Sector Count', 'sectors', 'wear',
- 'smartd_log.offline_uncorrectable_sector_count', 'line'],
- 'lines': [],
- 'attrs': [ATTR198],
- 'algo': ABSOLUTE,
- },
- 'percent_lifetime_used': {
- 'options': [None, 'Percent Lifetime Used', 'percentage', 'wear', 'smartd_log.percent_lifetime_used', 'line'],
- 'lines': [],
- 'attrs': [ATTR202],
- 'algo': ABSOLUTE,
- },
- 'media_wearout_indicator': {
- 'options': [None, 'Media Wearout Indicator', 'percentage', 'wear', 'smartd_log.media_wearout_indicator', 'line'],
- 'lines': [],
- 'attrs': [ATTR233],
- 'algo': ABSOLUTE,
- },
- 'nand_writes_1gib': {
- 'options': [None, 'NAND Writes', 'GiB', 'wear', 'smartd_log.nand_writes_1gib', 'line'],
- 'lines': [],
- 'attrs': [ATTR249],
- 'algo': ABSOLUTE,
- },
- }
- # NOTE: 'parse_temp' decodes ATA 194 raw value. Not heavily tested. Written by @Ferroin
- # C code:
- # https://github.com/smartmontools/smartmontools/blob/master/smartmontools/atacmds.cpp#L2051
- #
- # Calling 'parse_temp' on the raw value will return a 4-tuple, containing
- # * temperature
- # * minimum
- # * maximum
- # * over-temperature count
- # substituting None for values it can't decode.
- #
- # Example:
- # >>> parse_temp(42952491042)
- # >>> (34, 10, 43, None)
- #
- #
- # def check_temp_word(i):
- # if i <= 0x7F:
- # return 0x11
- # elif i <= 0xFF:
- # return 0x01
- # elif 0xFF80 <= i:
- # return 0x10
- # return 0x00
- #
- #
- # def check_temp_range(t, b0, b1):
- # if b0 > b1:
- # t0, t1 = b1, b0
- # else:
- # t0, t1 = b0, b1
- #
- # if all([
- # -60 <= t0,
- # t0 <= t,
- # t <= t1,
- # t1 <= 120,
- # not (t0 == -1 and t1 <= 0)
- # ]):
- # return t0, t1
- # return None, None
- #
- #
- # def parse_temp(raw):
- # byte = list()
- # word = list()
- # for i in range(0, 6):
- # byte.append(0xFF & (raw >> (i * 8)))
- # for i in range(0, 3):
- # word.append(0xFFFF & (raw >> (i * 16)))
- #
- # ctwd = check_temp_word(word[0])
- #
- # if not word[2]:
- # if ctwd and not word[1]:
- # # byte[0] is temp, no other data
- # return byte[0], None, None, None
- #
- # if ctwd and all(check_temp_range(byte[0], byte[2], byte[3])):
- # # byte[0] is temp, byte[2] is max or min, byte[3] is min or max
- # trange = check_temp_range(byte[0], byte[2], byte[3])
- # return byte[0], trange[0], trange[1], None
- #
- # if ctwd and all(check_temp_range(byte[0], byte[1], byte[2])):
- # # byte[0] is temp, byte[1] is max or min, byte[2] is min or max
- # trange = check_temp_range(byte[0], byte[1], byte[2])
- # return byte[0], trange[0], trange[1], None
- #
- # return None, None, None, None
- #
- # if ctwd:
- # if all(
- # [
- # ctwd & check_temp_word(word[1]) & check_temp_word(word[2]) != 0x00,
- # all(check_temp_range(byte[0], byte[2], byte[4])),
- # ]
- # ):
- # # byte[0] is temp, byte[2] is max or min, byte[4] is min or max
- # trange = check_temp_range(byte[0], byte[2], byte[4])
- # return byte[0], trange[0], trange[1], None
- # else:
- # trange = check_temp_range(byte[0], byte[2], byte[3])
- # if word[2] < 0x7FFF and all(trange) and trange[1] >= 40:
- # # byte[0] is temp, byte[2] is max or min, byte[3] is min or max, word[2] is overtemp count
- # return byte[0], trange[0], trange[1], word[2]
- # # no data
- # return None, None, None, None
- CHARTED_ATTRS = dict((attr, k) for k, v in CHARTS.items() for attr in v['attrs'])
- class BaseAtaSmartAttribute:
- def __init__(self, name, normalized_value, raw_value):
- self.name = name
- self.normalized_value = normalized_value
- self.raw_value = raw_value
- def value(self):
- raise NotImplementedError
- class AtaRaw(BaseAtaSmartAttribute):
- def value(self):
- return self.raw_value
- class AtaNormalized(BaseAtaSmartAttribute):
- def value(self):
- return self.normalized_value
- class Ata3(BaseAtaSmartAttribute):
- def value(self):
- value = int(self.raw_value)
- # https://github.com/netdata/netdata/issues/5919
- #
- # 3;151;38684000679;
- # 423 (Average 447)
- # 38684000679 & 0xFFF -> 423
- # (38684000679 & 0xFFF0000) >> 16 -> 447
- if value > 1e6:
- return value & 0xFFF
- return value
- class Ata9(BaseAtaSmartAttribute):
- def value(self):
- value = int(self.raw_value)
- if value > 1e6:
- return value & 0xFFFF
- return value
- class Ata190(BaseAtaSmartAttribute):
- def value(self):
- return 100 - int(self.normalized_value)
- class Ata194(BaseAtaSmartAttribute):
- # https://github.com/netdata/netdata/issues/3041
- # https://github.com/netdata/netdata/issues/5919
- #
- # The low byte is the current temperature, the third lowest is the maximum, and the fifth lowest is the minimum
- def value(self):
- value = int(self.raw_value)
- if value > 1e6:
- return value & 0xFF
- return min(int(self.normalized_value), int(self.raw_value))
- class BaseSCSISmartAttribute:
- def __init__(self, name, raw_value):
- self.name = name
- self.raw_value = raw_value
- def value(self):
- raise NotImplementedError
- class SCSIRaw(BaseSCSISmartAttribute):
- def value(self):
- return self.raw_value
- def ata_attribute_factory(value):
- name = value[0]
- if name == ATTR3:
- return Ata3(*value)
- elif name == ATTR9:
- return Ata9(*value)
- elif name == ATTR190:
- return Ata190(*value)
- elif name == ATTR194:
- return Ata194(*value)
- elif name in [
- ATTR1,
- ATTR7,
- ATTR202,
- ATTR206,
- ATTR233,
- ]:
- return AtaNormalized(*value)
- return AtaRaw(*value)
- def scsi_attribute_factory(value):
- return SCSIRaw(*value)
- def attribute_factory(value):
- name = value[0]
- if name.isdigit():
- return ata_attribute_factory(value)
- return scsi_attribute_factory(value)
- def handle_error(*errors):
- def on_method(method):
- def on_call(*args):
- try:
- return method(*args)
- except errors:
- return None
- return on_call
- return on_method
- class DiskLogFile:
- def __init__(self, full_path):
- self.path = full_path
- self.size = os.path.getsize(full_path)
- @handle_error(OSError)
- def is_changed(self):
- return self.size != os.path.getsize(self.path)
- @handle_error(OSError)
- def is_active(self, current_time, limit):
- return (current_time - os.path.getmtime(self.path)) / 60 < limit
- @handle_error(OSError)
- def read(self):
- self.size = os.path.getsize(self.path)
- return read_last_line(self.path)
- class BaseDisk:
- def __init__(self, name, log_file):
- self.raw_name = name
- self.name = re.sub(r'_+', '_', name)
- self.log_file = log_file
- self.attrs = list()
- self.alive = True
- self.charted = False
- def __eq__(self, other):
- if isinstance(other, BaseDisk):
- return self.raw_name == other.raw_name
- return self.raw_name == other
- def __ne__(self, other):
- return not self == other
- def __hash__(self):
- return hash(repr(self))
- def parser(self, data):
- raise NotImplementedError
- @handle_error(TypeError)
- def populate_attrs(self):
- self.attrs = list()
- line = self.log_file.read()
- for value in self.parser(line):
- self.attrs.append(attribute_factory(value))
- return len(self.attrs)
- def data(self):
- data = dict()
- for attr in self.attrs:
- data['{0}_{1}'.format(self.name, attr.name)] = attr.value()
- return data
- class ATADisk(BaseDisk):
- def parser(self, data):
- return RE_ATA.findall(data)
- class SCSIDisk(BaseDisk):
- def parser(self, data):
- return RE_SCSI.findall(data)
- class Service(SimpleService):
- def __init__(self, configuration=None, name=None):
- SimpleService.__init__(self, configuration=configuration, name=name)
- self.order = ORDER
- self.definitions = deepcopy(CHARTS)
- self.log_path = configuration.get('log_path', DEF_PATH)
- self.age = configuration.get('age', DEF_AGE)
- self.exclude = configuration.get('exclude_disks', str()).split()
- self.disks = list()
- self.runs = 0
- def check(self):
- return self.scan() > 0
- def get_data(self):
- self.runs += 1
- if self.runs % DEF_RESCAN_INTERVAL == 0:
- self.cleanup()
- self.scan()
- data = dict()
- for disk in self.disks:
- if not disk.alive:
- continue
- if not disk.charted:
- self.add_disk_to_charts(disk)
- changed = disk.log_file.is_changed()
- if changed is None:
- disk.alive = False
- continue
- if changed and disk.populate_attrs() is None:
- disk.alive = False
- continue
- data.update(disk.data())
- return data
- def cleanup(self):
- current_time = time()
- for disk in self.disks[:]:
- if any(
- [
- not disk.alive,
- not disk.log_file.is_active(current_time, self.age),
- ]
- ):
- self.disks.remove(disk.raw_name)
- self.remove_disk_from_charts(disk)
- def scan(self):
- self.debug('scanning {0}'.format(self.log_path))
- current_time = time()
- for full_name in os.listdir(self.log_path):
- disk = self.create_disk_from_file(full_name, current_time)
- if not disk:
- continue
- self.disks.append(disk)
- return len(self.disks)
- def create_disk_from_file(self, full_name, current_time):
- if not full_name.endswith(CSV):
- self.debug('skipping {0}: not a csv file'.format(full_name))
- return None
- name = os.path.basename(full_name).split('.')[-3]
- path = os.path.join(self.log_path, full_name)
- if name in self.disks:
- self.debug('skipping {0}: already in disks'.format(full_name))
- return None
- if [p for p in self.exclude if p in name]:
- self.debug('skipping {0}: filtered by `exclude` option'.format(full_name))
- return None
- if not os.access(path, os.R_OK):
- self.debug('skipping {0}: not readable'.format(full_name))
- return None
- if os.path.getsize(path) == 0:
- self.debug('skipping {0}: zero size'.format(full_name))
- return None
- if (current_time - os.path.getmtime(path)) / 60 > self.age:
- self.debug('skipping {0}: haven\'t been updated for last {1} minutes'.format(full_name, self.age))
- return None
- if ATA in full_name:
- disk = ATADisk(name, DiskLogFile(path))
- elif SCSI in full_name:
- disk = SCSIDisk(name, DiskLogFile(path))
- else:
- self.debug('skipping {0}: unknown type'.format(full_name))
- return None
- disk.populate_attrs()
- if not disk.attrs:
- self.error('skipping {0}: parsing failed'.format(full_name))
- return None
- self.debug('added {0}'.format(full_name))
- return disk
- def add_disk_to_charts(self, disk):
- if len(self.charts) == 0 or disk.charted:
- return
- disk.charted = True
- for attr in disk.attrs:
- chart_id = CHARTED_ATTRS.get(attr.name)
- if not chart_id or chart_id not in self.charts:
- continue
- chart = self.charts[chart_id]
- dim = [
- '{0}_{1}'.format(disk.name, attr.name),
- disk.name,
- CHARTS[chart_id]['algo'],
- ]
- if dim[0] in self.charts[chart_id].dimensions:
- chart.hide_dimension(dim[0], reverse=True)
- else:
- chart.add_dimension(dim)
- def remove_disk_from_charts(self, disk):
- if len(self.charts) == 0 or not disk.charted:
- return
- for attr in disk.attrs:
- chart_id = CHARTED_ATTRS.get(attr.name)
- if not chart_id or chart_id not in self.charts:
- continue
- self.charts[chart_id].del_dimension('{0}_{1}'.format(disk.name, attr.name))
|