From fa291c2a65b37debdd5bce3816d59a759e4cd142 Mon Sep 17 00:00:00 2001 From: lgz Date: Wed, 4 Jul 2018 23:32:54 +0900 Subject: ExecutableService: custom command support for _get_raw_data method --- python.d/python_modules/bases/FrameworkServices/ExecutableService.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python.d/python_modules/bases/FrameworkServices/ExecutableService.py b/python.d/python_modules/bases/FrameworkServices/ExecutableService.py index 9b2e945e79..95720e8b05 100644 --- a/python.d/python_modules/bases/FrameworkServices/ExecutableService.py +++ b/python.d/python_modules/bases/FrameworkServices/ExecutableService.py @@ -17,13 +17,13 @@ class ExecutableService(SimpleService): SimpleService.__init__(self, configuration=configuration, name=name) self.command = None - def _get_raw_data(self, stderr=False): + def _get_raw_data(self, stderr=False, command=None): """ Get raw data from executed command :return: """ try: - p = Popen(self.command, stdout=PIPE, stderr=PIPE) + p = Popen(command if command else self.command, stdout=PIPE, stderr=PIPE) except Exception as error: self.error('Executing command {command} resulted in error: {error}'.format(command=self.command, error=error)) -- cgit v1.2.3 From 17b153700acfb6df1c3b8defcc93e24e5826f793 Mon Sep 17 00:00:00 2001 From: lgz Date: Wed, 4 Jul 2018 23:33:06 +0900 Subject: megacli: initial version added --- python.d/megacli.chart.py | 171 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 python.d/megacli.chart.py diff --git a/python.d/megacli.chart.py b/python.d/megacli.chart.py new file mode 100644 index 0000000000..18bea21b9c --- /dev/null +++ b/python.d/megacli.chart.py @@ -0,0 +1,171 @@ +# -*- coding: utf-8 -*- +# Description: megacli netdata python.d module +# Author: Ilya Mashchenko (l2isbad) +# SPDX-License-Identifier: GPL-3.0+ + + +import re + +from bases.FrameworkServices.ExecutableService import ExecutableService +from bases.collection import find_binary + + +update_every = 5 + + +def adapter_charts(ads): + order = [ + 'adapter_degraded', + ] + + def dims(ad): + return [['adapter_{0}_degraded'.format(a.id), 'adapter {0}'.format(a.id)] for a in ad] + + charts = { + 'adapter_degraded': { + 'options': [ + None, 'Adapter State', 'is degraded', 'adapter', 'megacli.adapter_degraded', 'line'], + 'lines': dims(ads) + }, + } + + return order, charts + + +def pd_charts(pds): + order = [ + 'pd_media_error', + 'pd_predictive_failure', + ] + + def dims(k, pd): + return [['slot_{0}_{1}'.format(p.id, k), 'slot {0}'.format(p.id)] for p in pd] + + charts = { + 'pd_media_error': { + 'options': [ + None, 'Physical Drives Media Errors', 'errors/s', 'pd', 'megacli.pd_media_error', 'line' + ], + 'lines': dims("media_error", pds)}, + 'pd_predictive_failure': { + 'options': [ + None, 'Physical Drives Predictive Failures', 'failures/s', 'pd', 'megacli.pd_predictive_failure', 'line' + ], + 'lines': dims("predictive_failure", pds)} + } + + return order, charts + + +RE_ADAPTER = re.compile( + r'Adapter #([0-9]+) State\s+: ([a-zA-Z]+)' +) + +RE_VD = re.compile( + r'Slot Number: ([0-9]+) Media Error Count: ([0-9]+) Predictive Failure Count: ([0-9]+)' +) + + +def find_adapters(d): + keys = ("Adapter #", "State") + d = ' '.join(v.strip() for v in d if v.startswith(keys)) + return [Adapter(*v) for v in RE_ADAPTER.findall(d)] + + +def find_pds(d): + keys = ("Slot Number", "Media Error Count", "Predictive Failure Count") + d = ' '.join(v.strip() for v in d if v.startswith(keys)) + return [PD(*v) for v in RE_VD.findall(d)] + + +class Adapter: + def __init__(self, n, state): + self.id = n + self.state = int(state == 'Degraded') + + def data(self): + return { + 'adapter_{0}_degraded'.format(self.id): self.state, + } + + +class PD: + def __init__(self, n, media_err, predict_fail): + self.id = n + self.media_err = media_err + self.predict_fail = predict_fail + + def data(self): + return { + 'slot_{0}_media_error'.format(self.id): self.media_err, + 'slot_{0}_predictive_failure'.format(self.id): self.predict_fail, + } + + +class Command: + def __init__(self): + self.command = find_binary('megacli') + self.disk_info = [self.command, '-LDPDInfo', '-aAll'] + self.battery_info = [self.command, '-AdpBbuCmd', '-a0'] + + def __bool__(self): + return bool(self.command) + + def __nonzero__(self): + return self.__bool__() + + +class Service(ExecutableService): + def __init__(self, configuration=None, name=None): + ExecutableService.__init__(self, configuration=configuration, name=name) + self.order = list() + self.definitions = dict() + self.command = Command() + + def check(self): + if not self.command: + self.error("can't locate \"megacli\" binary or binary is not executable by netdata") + return None + + d = self._get_raw_data(command=self.command.disk_info) + + if not d: + return None + + ads = find_adapters(d) + pds = find_pds(d) + + if not (ads and pds): + self.error('failed to parse "{0}" output'.format(' '.join(self.command.disk_info))) + return None + + self.create_charts(ads, pds) + return True + + def get_data(self): + return self.get_adapter_pd_data() + + def get_adapter_pd_data(self): + raw = self._get_raw_data(command=self.command.disk_info) + + if not raw: + return None + + data = dict() + + for a in find_adapters(raw): + data.update(a.data()) + + for p in find_pds(raw): + data.update(p.data()) + + return data + + def create_charts(self, ads, pds): + o, c = adapter_charts(ads) + self.order.extend(o) + self.definitions.update(c) + + o, c = pd_charts(pds) + self.order.extend(o) + self.definitions.update(c) -- cgit v1.2.3 From 35f27b08a1dae759039398d7c90e5f2e4baf5821 Mon Sep 17 00:00:00 2001 From: lgz Date: Wed, 4 Jul 2018 23:33:24 +0900 Subject: megacli: added to makefile --- python.d/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/python.d/Makefile.am b/python.d/Makefile.am index caf4a6210b..43fcef6c69 100644 --- a/python.d/Makefile.am +++ b/python.d/Makefile.am @@ -41,6 +41,7 @@ dist_python_DATA = \ litespeed.chart.py \ logind.chart.py \ mdstat.chart.py \ + megacli.chart.py \ memcached.chart.py \ mongodb.chart.py \ mysql.chart.py \ -- cgit v1.2.3 From 572997027f6e9f738a01fc28c279ed374d4ae90c Mon Sep 17 00:00:00 2001 From: lgz Date: Wed, 4 Jul 2018 23:49:48 +0900 Subject: megacli: pds dims => incremental --- python.d/megacli.chart.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python.d/megacli.chart.py b/python.d/megacli.chart.py index 18bea21b9c..08bcde0b94 100644 --- a/python.d/megacli.chart.py +++ b/python.d/megacli.chart.py @@ -39,7 +39,7 @@ def pd_charts(pds): ] def dims(k, pd): - return [['slot_{0}_{1}'.format(p.id, k), 'slot {0}'.format(p.id)] for p in pd] + return [['slot_{0}_{1}'.format(p.id, k), 'slot {0}'.format(p.id), 'incremental'] for p in pd] charts = { 'pd_media_error': { -- cgit v1.2.3 From c026b9c12933d8edccdc40455c262f7fd0f7a014 Mon Sep 17 00:00:00 2001 From: lgz Date: Thu, 5 Jul 2018 20:18:30 +0900 Subject: megacli: rename command => megacli --- python.d/megacli.chart.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/python.d/megacli.chart.py b/python.d/megacli.chart.py index 08bcde0b94..ca808eb0dc 100644 --- a/python.d/megacli.chart.py +++ b/python.d/megacli.chart.py @@ -120,14 +120,14 @@ class Service(ExecutableService): ExecutableService.__init__(self, configuration=configuration, name=name) self.order = list() self.definitions = dict() - self.command = Command() + self.megacli = Command() def check(self): - if not self.command: + if not self.megacli: self.error("can't locate \"megacli\" binary or binary is not executable by netdata") return None - d = self._get_raw_data(command=self.command.disk_info) + d = self._get_raw_data(command=self.megacli.disk_info) if not d: return None @@ -136,7 +136,7 @@ class Service(ExecutableService): pds = find_pds(d) if not (ads and pds): - self.error('failed to parse "{0}" output'.format(' '.join(self.command.disk_info))) + self.error('failed to parse "{0}" output'.format(' '.join(self.megacli.disk_info))) return None self.create_charts(ads, pds) @@ -146,7 +146,7 @@ class Service(ExecutableService): return self.get_adapter_pd_data() def get_adapter_pd_data(self): - raw = self._get_raw_data(command=self.command.disk_info) + raw = self._get_raw_data(command=self.megacli.disk_info) if not raw: return None -- cgit v1.2.3 From 8d033934bd24e3f48984853a485dfb727d6154f8 Mon Sep 17 00:00:00 2001 From: lgz Date: Thu, 5 Jul 2018 20:40:26 +0900 Subject: megacli: run megacli via sudo --- python.d/megacli.chart.py | 22 +++++++++++++++------- .../bases/FrameworkServices/ExecutableService.py | 2 +- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/python.d/megacli.chart.py b/python.d/megacli.chart.py index ca808eb0dc..58241a1e89 100644 --- a/python.d/megacli.chart.py +++ b/python.d/megacli.chart.py @@ -102,14 +102,16 @@ class PD: } -class Command: +class Megacli: def __init__(self): - self.command = find_binary('megacli') - self.disk_info = [self.command, '-LDPDInfo', '-aAll'] - self.battery_info = [self.command, '-AdpBbuCmd', '-a0'] + self.s = find_binary('sudo') + self.m = find_binary('megacli') + self.sudo_check = [self.s, '-n', '-v'] + self.disk_info = [self.s, '-n', self.m, '-LDPDInfo', '-aAll'] + self.battery_info = [self.s, '-n', self.m, '-AdpBbuCmd', '-a0'] def __bool__(self): - return bool(self.command) + return bool(self.s and self.m) def __nonzero__(self): return self.__bool__() @@ -120,13 +122,19 @@ class Service(ExecutableService): ExecutableService.__init__(self, configuration=configuration, name=name) self.order = list() self.definitions = dict() - self.megacli = Command() + self.megacli = Megacli() def check(self): if not self.megacli: - self.error("can't locate \"megacli\" binary or binary is not executable by netdata") + self.error('can\'t locate "sudo" or "megacli" binary') return None + err = self._get_raw_data(command=self.megacli.sudo_check, stderr=True) + + if err: + self.error(''.join(err)) + return False + d = self._get_raw_data(command=self.megacli.disk_info) if not d: diff --git a/python.d/python_modules/bases/FrameworkServices/ExecutableService.py b/python.d/python_modules/bases/FrameworkServices/ExecutableService.py index 95720e8b05..625f643234 100644 --- a/python.d/python_modules/bases/FrameworkServices/ExecutableService.py +++ b/python.d/python_modules/bases/FrameworkServices/ExecutableService.py @@ -25,7 +25,7 @@ class ExecutableService(SimpleService): try: p = Popen(command if command else self.command, stdout=PIPE, stderr=PIPE) except Exception as error: - self.error('Executing command {command} resulted in error: {error}'.format(command=self.command, + self.error('Executing command {command} resulted in error: {error}'.format(command=command or self.command, error=error)) return None data = list() -- cgit v1.2.3 From 82383e6f6cc697d5ffe539d803a183803ef6228a Mon Sep 17 00:00:00 2001 From: lgz Date: Fri, 6 Jul 2018 13:48:31 +0900 Subject: megacli: add battery stats --- python.d/megacli.chart.py | 130 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 110 insertions(+), 20 deletions(-) diff --git a/python.d/megacli.chart.py b/python.d/megacli.chart.py index 58241a1e89..83450a719a 100644 --- a/python.d/megacli.chart.py +++ b/python.d/megacli.chart.py @@ -57,6 +57,31 @@ def pd_charts(pds): return order, charts +def battery_charts(bats): + order = [ + 'relative_charge', + 'cycle_count', + ] + + def dims(k, bat): + return [['battery_{0}_{1}'.format(b.id, k), 'adapter {0}'.format(b.id)] for b in bat] + + charts = { + 'relative_charge': { + 'options': [ + None, 'Relative State of Charge', '%', 'battery', 'megacli.relative_charge', 'line'], + 'lines': dims("relative_charge", bats) + }, + 'cycle_count': { + 'options': [ + None, 'Cycle Count', 'cycle count', 'battery', 'megacli.cycle_count', 'line'], + 'lines': dims("cycle_count", bats) + }, + } + + return order, charts + + RE_ADAPTER = re.compile( r'Adapter #([0-9]+) State\s+: ([a-zA-Z]+)' ) @@ -65,6 +90,10 @@ RE_VD = re.compile( r'Slot Number: ([0-9]+) Media Error Count: ([0-9]+) Predictive Failure Count: ([0-9]+)' ) +RE_BATTERY = re.compile( + r'BBU Capacity Info for Adapter: ([0-9]+) Relative State of Charge: ([0-9]+) % Cycle Count: ([0-5]+)' +) + def find_adapters(d): keys = ("Adapter #", "State") @@ -78,6 +107,12 @@ def find_pds(d): return [PD(*v) for v in RE_VD.findall(d)] +def find_batteries(d): + keys = ('BBU Capacity Info for Adapter', 'Relative State of Charge', 'Cycle Count') + d = ' '.join(v.strip() for v in d if v.startswith(keys)) + return [Battery(*v) for v in RE_BATTERY.findall(d)] + + class Adapter: def __init__(self, n, state): self.id = n @@ -102,6 +137,20 @@ class PD: } +class Battery: + def __init__(self, adapt_id, rel_charge, cycle_count): + self.id = adapt_id + self.rel_charge = rel_charge + self.cycle_count = cycle_count + + def data(self): + return { + 'battery_{0}_relative_charge'.format(self.id): self.rel_charge, + 'battery_{0}_cycle_count'.format(self.id): self.cycle_count, + } + + +# TODO: hardcoded sudo... class Megacli: def __init__(self): self.s = find_binary('sudo') @@ -123,43 +172,81 @@ class Service(ExecutableService): self.order = list() self.definitions = dict() self.megacli = Megacli() + self.do_battery = self.configuration.get('do_battery') - def check(self): - if not self.megacli: - self.error('can\'t locate "sudo" or "megacli" binary') - return None - + def check_sudo(self): err = self._get_raw_data(command=self.megacli.sudo_check, stderr=True) - if err: self.error(''.join(err)) return False + return True + def check_disk_info(self): d = self._get_raw_data(command=self.megacli.disk_info) - if not d: - return None + return False ads = find_adapters(d) pds = find_pds(d) if not (ads and pds): self.error('failed to parse "{0}" output'.format(' '.join(self.megacli.disk_info))) + return False + + o, c = adapter_charts(ads) + self.order.extend(o) + self.definitions.update(c) + + o, c = pd_charts(pds) + self.order.extend(o) + self.definitions.update(c) + + return True + + def check_battery(self): + d = self._get_raw_data(command=self.megacli.battery_info) + if not d: + return + + bats = find_batteries(d) + + if not bats: + self.error('failed to parse "{0}" output'.format(' '.join(self.megacli.battery_info))) + return + + o, c = battery_charts(bats) + self.order.extend(o) + self.definitions.update(c) + + def check(self): + if not self.megacli: + self.error('can\'t locate "sudo" or "megacli" binary') return None - self.create_charts(ads, pds) + if not (self.check_sudo() and self.check_disk_info()): + return False + + if self.do_battery: + self.check_battery() + return True def get_data(self): - return self.get_adapter_pd_data() + data = dict() + + data.update(self.get_adapter_pd_data()) + + if self.do_battery: + data.update(self.get_battery_data()) + + return data or None def get_adapter_pd_data(self): raw = self._get_raw_data(command=self.megacli.disk_info) + data = dict() if not raw: - return None - - data = dict() + return data for a in find_adapters(raw): data.update(a.data()) @@ -169,11 +256,14 @@ class Service(ExecutableService): return data - def create_charts(self, ads, pds): - o, c = adapter_charts(ads) - self.order.extend(o) - self.definitions.update(c) + def get_battery_data(self): + raw = self._get_raw_data(command=self.megacli.battery_info) + data = dict() - o, c = pd_charts(pds) - self.order.extend(o) - self.definitions.update(c) + if not raw: + return data + + for b in find_batteries(raw): + data.update(b.data()) + + return data -- cgit v1.2.3 From e1d2c6de031f841fb4586436f50dfc975a127c72 Mon Sep 17 00:00:00 2001 From: lgz Date: Fri, 6 Jul 2018 15:06:19 +0900 Subject: megacli: add configuration file --- conf.d/Makefile.am | 1 + conf.d/python.d/megacli.conf | 68 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 conf.d/python.d/megacli.conf diff --git a/conf.d/Makefile.am b/conf.d/Makefile.am index 72807b6804..4cf0cca027 100644 --- a/conf.d/Makefile.am +++ b/conf.d/Makefile.am @@ -52,6 +52,7 @@ dist_pythonconfig_DATA = \ python.d/isc_dhcpd.conf \ python.d/litespeed.conf \ python.d/logind.conf \ + python.d/megacli.conf \ python.d/mdstat.conf \ python.d/memcached.conf \ python.d/mongodb.conf \ diff --git a/conf.d/python.d/megacli.conf b/conf.d/python.d/megacli.conf new file mode 100644 index 0000000000..d84078ecb1 --- /dev/null +++ b/conf.d/python.d/megacli.conf @@ -0,0 +1,68 @@ +# netdata python.d.plugin configuration for megacli +# +# This file is in YaML format. Generally the format is: +# +# name: value +# + +# ---------------------------------------------------------------------- +# Global Variables +# These variables set the defaults for all JOBs, however each JOB +# may define its own, overriding the defaults. + +# update_every sets the default data collection frequency. +# If unset, the python.d.plugin default is used. +# update_every: 1 + +# priority controls the order of charts at the netdata dashboard. +# Lower numbers move the charts towards the top of the page. +# If unset, the default for python.d.plugin is used. +# priority: 60000 + +# retries sets the number of retries to be made in case of failures. +# If unset, the default for python.d.plugin is used. +# Attempts to restore the service are made once every update_every +# and only if the module has collected values in the past. +# retries: 60 + +# autodetection_retry sets the job re-check interval in seconds. +# The job is not deleted if check fails. +# Attempts to start the job are made once every autodetection_retry. +# This feature is disabled by default. +# autodetection_retry: 0 + +# ---------------------------------------------------------------------- +# JOBS (data collection sources) +# +# The default JOBS share the same *name*. JOBS with the same name +# are mutually exclusive. Only one of them will be allowed running at +# any time. This allows autodetection to try several alternatives and +# pick the one that works. +# +# Any number of jobs is supported. +# +# All python.d.plugin JOBS (for all its modules) support a set of +# predefined parameters. These are: +# +# job_name: +# name: myname # the JOB's name as it will appear at the +# # dashboard (by default is the job_name) +# # JOBs sharing a name are mutually exclusive +# update_every: 1 # the JOB's data collection frequency +# priority: 60000 # the JOB's order on the dashboard +# retries: 60 # the JOB's number of restoration attempts +# autodetection_retry: 0 # the JOB's re-check interval in seconds +# +# Additionally to the above, megacli also supports the following: +# +# do_battery: yes/no # default is no. Battery stats (adds additional call to megacli `megacli -AdpBbuCmd -a0`). +# +# ---------------------------------------------------------------------- + +# IMPORTANT +# The netdata user needs to be able to be able to sudo the megacli program without password: +# netdata ALL=(root) NOPASSWD: /path/to/megacli + + +# uncomment the line below to collect battery statistics +# do_battery: yes -- cgit v1.2.3 From 8c40989ed437b05f45dcdf4c8d23787ce691320c Mon Sep 17 00:00:00 2001 From: lgz Date: Fri, 6 Jul 2018 17:07:03 +0900 Subject: megacli: add module python.d README --- python.d/README.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/python.d/README.md b/python.d/README.md index 2417fc4302..392c9eca78 100644 --- a/python.d/README.md +++ b/python.d/README.md @@ -1114,6 +1114,35 @@ is currently disabled by default, and needs to be explicitly enabled in --- +# megacli + +Module collects adapter, physical drives and battery stats. + +**Requirements:** + * `netdata` user needs to be able to be able to sudo the `megacli` program without password + +To grab stats it following executes: + * `sudo -n megacli -LDPDInfo -aAll` + * `sudo -n megacli -AdpBbuCmd -a0` + + +It produces: + +1. **Adapter State** + +2. **Physical Drives Media Errors** + +3. **Physical Drives Predictive Failures** + +4. **Battery Relative State of Charge** + +5. **Battery Cycle Count** + +### configuration +Battery stats disabled by default in the module configuration file. + +--- + # mdstat Module monitor /proc/mdstat -- cgit v1.2.3 From beb20ad334396a3611689c4c3885d0b6253452a2 Mon Sep 17 00:00:00 2001 From: lgz Date: Fri, 6 Jul 2018 17:08:57 +0900 Subject: megacli: add module python.d README --- python.d/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python.d/README.md b/python.d/README.md index 392c9eca78..6a59de8541 100644 --- a/python.d/README.md +++ b/python.d/README.md @@ -1121,7 +1121,7 @@ Module collects adapter, physical drives and battery stats. **Requirements:** * `netdata` user needs to be able to be able to sudo the `megacli` program without password -To grab stats it following executes: +To grab stats it executes: * `sudo -n megacli -LDPDInfo -aAll` * `sudo -n megacli -AdpBbuCmd -a0` -- cgit v1.2.3 From 0d27f5313c9f5a7aaa0654a74de0d638a7fe7e97 Mon Sep 17 00:00:00 2001 From: lgz Date: Fri, 6 Jul 2018 18:01:42 +0900 Subject: megacli: remove call to battery info on every iter if it fails on check --- python.d/megacli.chart.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/python.d/megacli.chart.py b/python.d/megacli.chart.py index 83450a719a..97e7bfd173 100644 --- a/python.d/megacli.chart.py +++ b/python.d/megacli.chart.py @@ -206,17 +206,18 @@ class Service(ExecutableService): def check_battery(self): d = self._get_raw_data(command=self.megacli.battery_info) if not d: - return + return False bats = find_batteries(d) if not bats: self.error('failed to parse "{0}" output'.format(' '.join(self.megacli.battery_info))) - return + return False o, c = battery_charts(bats) self.order.extend(o) self.definitions.update(c) + return True def check(self): if not self.megacli: @@ -227,7 +228,7 @@ class Service(ExecutableService): return False if self.do_battery: - self.check_battery() + self.do_battery = self.check_battery() return True -- cgit v1.2.3 From 4574e92f7ee635b36402185c1afab3b2a2e99af4 Mon Sep 17 00:00:00 2001 From: lgz Date: Fri, 6 Jul 2018 19:06:44 +0900 Subject: megacli: battery parse fix --- python.d/megacli.chart.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python.d/megacli.chart.py b/python.d/megacli.chart.py index 97e7bfd173..672f024312 100644 --- a/python.d/megacli.chart.py +++ b/python.d/megacli.chart.py @@ -91,7 +91,7 @@ RE_VD = re.compile( ) RE_BATTERY = re.compile( - r'BBU Capacity Info for Adapter: ([0-9]+) Relative State of Charge: ([0-9]+) % Cycle Count: ([0-5]+)' + r'BBU Capacity Info for Adapter: ([0-9]+) Relative State of Charge: ([0-9]+) % Cycle Count: ([0-9]+)' ) @@ -109,7 +109,7 @@ def find_pds(d): def find_batteries(d): keys = ('BBU Capacity Info for Adapter', 'Relative State of Charge', 'Cycle Count') - d = ' '.join(v.strip() for v in d if v.startswith(keys)) + d = ' '.join(v.strip() for v in d if v.strip().startswith(keys)) return [Battery(*v) for v in RE_BATTERY.findall(d)] -- cgit v1.2.3 From 7dba68d0e2d815e85102fdb76a2d87c126b28347 Mon Sep 17 00:00:00 2001 From: lgz Date: Sat, 7 Jul 2018 12:37:28 +0900 Subject: megacli: create separate chart for every bbu (needed for aralms) --- python.d/megacli.chart.py | 54 ++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/python.d/megacli.chart.py b/python.d/megacli.chart.py index 672f024312..594c4fb17b 100644 --- a/python.d/megacli.chart.py +++ b/python.d/megacli.chart.py @@ -58,26 +58,36 @@ def pd_charts(pds): def battery_charts(bats): - order = [ - 'relative_charge', - 'cycle_count', - ] - - def dims(k, bat): - return [['battery_{0}_{1}'.format(b.id, k), 'adapter {0}'.format(b.id)] for b in bat] - - charts = { - 'relative_charge': { - 'options': [ - None, 'Relative State of Charge', '%', 'battery', 'megacli.relative_charge', 'line'], - 'lines': dims("relative_charge", bats) - }, - 'cycle_count': { - 'options': [ - None, 'Cycle Count', 'cycle count', 'battery', 'megacli.cycle_count', 'line'], - 'lines': dims("cycle_count", bats) - }, - } + order = list() + charts = dict() + + for b in bats: + order.append('bbu_{0}_relative_charge'.format(b.id)) + charts.update( + { + 'bbu_{0}_relative_charge'.format(b.id): { + 'options': [ + None, 'Relative State of Charge', '%', 'battery', 'megacli.bbu_relative_charge', 'line'], + 'lines': [ + ['bbu_{0}_relative_charge'.format(b.id), 'adapter {0}'.format(b.id)], + ] + } + } + ) + + for b in bats: + order.append('bbu_{0}_cycle_count'.format(b.id)) + charts.update( + { + 'bbu_{0}_cycle_count'.format(b.id): { + 'options': [ + None, 'Cycle Count', 'cycle count', 'battery', 'megacli.bbu_cycle_count', 'line'], + 'lines': [ + ['bbu_{0}_cycle_count'.format(b.id), 'adapter {0}'.format(b.id)], + ] + } + } + ) return order, charts @@ -145,8 +155,8 @@ class Battery: def data(self): return { - 'battery_{0}_relative_charge'.format(self.id): self.rel_charge, - 'battery_{0}_cycle_count'.format(self.id): self.cycle_count, + 'bbu_{0}_relative_charge'.format(self.id): self.rel_charge, + 'bbu_{0}_cycle_count'.format(self.id): self.cycle_count, } -- cgit v1.2.3 From 1b2d2658ff73d392ad3923514327edd5763b1f73 Mon Sep 17 00:00:00 2001 From: lgz Date: Sat, 7 Jul 2018 12:37:47 +0900 Subject: megacli: alarms added --- conf.d/Makefile.am | 3 ++- conf.d/health.d/megacli.conf | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 conf.d/health.d/megacli.conf diff --git a/conf.d/Makefile.am b/conf.d/Makefile.am index 4cf0cca027..4e692266b7 100644 --- a/conf.d/Makefile.am +++ b/conf.d/Makefile.am @@ -52,8 +52,8 @@ dist_pythonconfig_DATA = \ python.d/isc_dhcpd.conf \ python.d/litespeed.conf \ python.d/logind.conf \ - python.d/megacli.conf \ python.d/mdstat.conf \ + python.d/megacli.conf \ python.d/memcached.conf \ python.d/mongodb.conf \ python.d/mysql.conf \ @@ -111,6 +111,7 @@ dist_healthconfig_DATA = \ health.d/isc_dhcpd.conf \ health.d/lighttpd.conf \ health.d/mdstat.conf \ + health.d/megacli.conf \ health.d/memcached.conf \ health.d/memory.conf \ health.d/mongodb.conf \ diff --git a/conf.d/health.d/megacli.conf b/conf.d/health.d/megacli.conf new file mode 100644 index 0000000000..a2646b3e80 --- /dev/null +++ b/conf.d/health.d/megacli.conf @@ -0,0 +1,28 @@ + alarm: adapter_state + on: megacli.adapter_degraded + units: is degraded + lookup: sum -10s + every: 10s + crit: $this > 0 + info: adapter state + to: sysadmin + + template: bbu_relative_charge + on: megacli.bbu_relative_charge + units: percent + lookup: average -10s + every: 10s + warn: $this <= (($status >= $WARNING) ? (85) : (80)) + crit: $this <= (($status == $CRITICAL) ? (50) : (40)) + info: BBU relative state of charge + to: sysadmin + + template: bbu_cycle_count + on: megacli.bbu_cycle_count + units: cycle count + lookup: average -10s + every: 10s + warn: $this >= 100 + crit: $this >= 500 + info: BBU cycle count + to: sysadmin -- cgit v1.2.3 From b828721ebf824258e8f632597308bf4a0d8ce726 Mon Sep 17 00:00:00 2001 From: lgz Date: Sat, 7 Jul 2018 12:39:13 +0900 Subject: python.d README: move megacli below mdstat --- python.d/README.md | 54 +++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/python.d/README.md b/python.d/README.md index 6a59de8541..3a246cfbd1 100644 --- a/python.d/README.md +++ b/python.d/README.md @@ -1114,6 +1114,33 @@ is currently disabled by default, and needs to be explicitly enabled in --- +# mdstat + +Module monitor /proc/mdstat + +It produces: + +1. **Health** Number of failed disks in every array (aggregate chart). + +2. **Disks stats** + * total (number of devices array ideally would have) + * inuse (number of devices currently are in use) + +3. **Current status** + * resync in percent + * recovery in percent + * reshape in percent + * check in percent + +4. **Operation status** (if resync/recovery/reshape/check is active) + * finish in minutes + * speed in megabytes/s + +### configuration +No configuration is needed. + +--- + # megacli Module collects adapter, physical drives and battery stats. @@ -1143,33 +1170,6 @@ Battery stats disabled by default in the module configuration file. --- -# mdstat - -Module monitor /proc/mdstat - -It produces: - -1. **Health** Number of failed disks in every array (aggregate chart). - -2. **Disks stats** - * total (number of devices array ideally would have) - * inuse (number of devices currently are in use) - -3. **Current status** - * resync in percent - * recovery in percent - * reshape in percent - * check in percent - -4. **Operation status** (if resync/recovery/reshape/check is active) - * finish in minutes - * speed in megabytes/s - -### configuration -No configuration is needed. - ---- - # memcached Memcached monitoring module. Data grabbed from [stats interface](https://github.com/memcached/memcached/wiki/Commands#stats). -- cgit v1.2.3 From 81581660c72a0b4d0e9edcdcdbf920717200ab60 Mon Sep 17 00:00:00 2001 From: lgz Date: Sat, 7 Jul 2018 12:57:29 +0900 Subject: megacli: add media err and predict failure alarms --- conf.d/health.d/megacli.conf | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/conf.d/health.d/megacli.conf b/conf.d/health.d/megacli.conf index a2646b3e80..1881a7be14 100644 --- a/conf.d/health.d/megacli.conf +++ b/conf.d/health.d/megacli.conf @@ -26,3 +26,23 @@ crit: $this >= 500 info: BBU cycle count to: sysadmin + + alarm: pd_media_errors + on: megacli.pd_media_error + units: media errors + lookup: sum -10s + every: 10s + warn: $this > 0 + delay: down 1m multiplier 2 max 10m + info: physical drive media errors + to: sysadmin + + alarm: pd_predictive_failures + on: megacli.pd_predictive_failure + units: predictive failures + lookup: sum -10s + every: 10s + warn: $this > 0 + delay: down 1m multiplier 2 max 10m + info: physical drive predictive failures + to: sysadmin -- cgit v1.2.3