123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- # -*- coding: utf-8 -*-
- # Description: adaptec_raid netdata python.d module
- # Author: Ilya Mashchenko (ilyam8)
- # SPDX-License-Identifier: GPL-3.0-or-later
- import re
- from copy import deepcopy
- from bases.FrameworkServices.ExecutableService import ExecutableService
- from bases.collection import find_binary
- disabled_by_default = True
- update_every = 5
- ORDER = [
- 'ld_status',
- 'pd_state',
- 'pd_smart_warnings',
- 'pd_temperature',
- ]
- CHARTS = {
- 'ld_status': {
- 'options': [None, 'Status of logical devices (1: Failed or Degraded)', 'bool', 'logical devices',
- 'adaptec_raid.ld_status', 'line'],
- 'lines': []
- },
- 'pd_state': {
- 'options': [None, 'State of physical devices (1: not Online)', 'bool', 'physical devices',
- 'adaptec_raid.pd_state', 'line'],
- 'lines': []
- },
- 'pd_smart_warnings': {
- 'options': [None, 'S.M.A.R.T warnings', 'count', 'physical devices',
- 'adaptec_raid.smart_warnings', 'line'],
- 'lines': []
- },
- 'pd_temperature': {
- 'options': [None, 'Temperature', 'celsius', 'physical devices', 'adaptec_raid.temperature', 'line'],
- 'lines': []
- },
- }
- SUDO = 'sudo'
- ARCCONF = 'arcconf'
- BAD_LD_STATUS = (
- 'Degraded',
- 'Failed',
- )
- GOOD_PD_STATUS = (
- 'Online',
- )
- RE_LD = re.compile(
- r'Logical [dD]evice number\s+([0-9]+).*?'
- r'Status of [lL]ogical [dD]evice\s+: ([a-zA-Z]+)'
- )
- def find_lds(d):
- d = ' '.join(v.strip() for v in d)
- return [LD(*v) for v in RE_LD.findall(d)]
- def find_pds(d):
- pds = list()
- pd = PD()
- for row in d:
- row = row.strip()
- if row.startswith('Device #'):
- pd = PD()
- pd.id = row.split('#')[-1]
- elif not pd.id:
- continue
- if row.startswith('State'):
- v = row.split()[-1]
- pd.state = v
- elif row.startswith('S.M.A.R.T. warnings'):
- v = row.split()[-1]
- pd.smart_warnings = v
- elif row.startswith('Temperature'):
- v = row.split(':')[-1].split()[0]
- pd.temperature = v
- elif row.startswith('NCQ status'):
- if pd.id and pd.state and pd.smart_warnings:
- pds.append(pd)
- pd = PD()
- return pds
- class LD:
- def __init__(self, ld_id, status):
- self.id = ld_id
- self.status = status
- def data(self):
- return {
- 'ld_{0}_status'.format(self.id): int(self.status in BAD_LD_STATUS)
- }
- class PD:
- def __init__(self):
- self.id = None
- self.state = None
- self.smart_warnings = None
- self.temperature = None
- def data(self):
- data = {
- 'pd_{0}_state'.format(self.id): int(self.state not in GOOD_PD_STATUS),
- 'pd_{0}_smart_warnings'.format(self.id): self.smart_warnings,
- }
- if self.temperature and self.temperature.isdigit():
- data['pd_{0}_temperature'.format(self.id)] = self.temperature
- return data
- class Arcconf:
- def __init__(self, arcconf):
- self.arcconf = arcconf
- def ld_info(self):
- return [self.arcconf, 'GETCONFIG', '1', 'LD']
- def pd_info(self):
- return [self.arcconf, 'GETCONFIG', '1', 'PD']
- # TODO: hardcoded sudo...
- class SudoArcconf:
- def __init__(self, arcconf, sudo):
- self.arcconf = Arcconf(arcconf)
- self.sudo = sudo
- def ld_info(self):
- return [self.sudo, '-n'] + self.arcconf.ld_info()
- def pd_info(self):
- return [self.sudo, '-n'] + self.arcconf.pd_info()
- class Service(ExecutableService):
- def __init__(self, configuration=None, name=None):
- ExecutableService.__init__(self, configuration=configuration, name=name)
- self.order = ORDER
- self.definitions = deepcopy(CHARTS)
- self.use_sudo = self.configuration.get('use_sudo', True)
- self.arcconf = None
- def execute(self, command, stderr=False):
- return self._get_raw_data(command=command, stderr=stderr)
- def check(self):
- arcconf = find_binary(ARCCONF)
- if not arcconf:
- self.error('can\'t locate "{0}" binary'.format(ARCCONF))
- return False
- sudo = find_binary(SUDO)
- if self.use_sudo:
- if not sudo:
- self.error('can\'t locate "{0}" binary'.format(SUDO))
- return False
- err = self.execute([sudo, '-n', '-v'], True)
- if err:
- self.error(' '.join(err))
- return False
- if self.use_sudo:
- self.arcconf = SudoArcconf(arcconf, sudo)
- else:
- self.arcconf = Arcconf(arcconf)
- lds = self.get_lds()
- if not lds:
- return False
- self.debug('discovered logical devices ids: {0}'.format([ld.id for ld in lds]))
- pds = self.get_pds()
- if not pds:
- return False
- self.debug('discovered physical devices ids: {0}'.format([pd.id for pd in pds]))
- self.update_charts(lds, pds)
- return True
- def get_data(self):
- data = dict()
- for ld in self.get_lds():
- data.update(ld.data())
- for pd in self.get_pds():
- data.update(pd.data())
- return data
- def get_lds(self):
- raw_lds = self.execute(self.arcconf.ld_info())
- if not raw_lds:
- return None
- lds = find_lds(raw_lds)
- if not lds:
- self.error('failed to parse "{0}" output'.format(' '.join(self.arcconf.ld_info())))
- self.debug('output: {0}'.format(raw_lds))
- return None
- return lds
- def get_pds(self):
- raw_pds = self.execute(self.arcconf.pd_info())
- if not raw_pds:
- return None
- pds = find_pds(raw_pds)
- if not pds:
- self.error('failed to parse "{0}" output'.format(' '.join(self.arcconf.pd_info())))
- self.debug('output: {0}'.format(raw_pds))
- return None
- return pds
- def update_charts(self, lds, pds):
- charts = self.definitions
- for ld in lds:
- dim = ['ld_{0}_status'.format(ld.id), 'ld {0}'.format(ld.id)]
- charts['ld_status']['lines'].append(dim)
- for pd in pds:
- dim = ['pd_{0}_state'.format(pd.id), 'pd {0}'.format(pd.id)]
- charts['pd_state']['lines'].append(dim)
- dim = ['pd_{0}_smart_warnings'.format(pd.id), 'pd {0}'.format(pd.id)]
- charts['pd_smart_warnings']['lines'].append(dim)
- dim = ['pd_{0}_temperature'.format(pd.id), 'pd {0}'.format(pd.id)]
- charts['pd_temperature']['lines'].append(dim)
|