summaryrefslogtreecommitdiffstats
path: root/collectors/python.d.plugin/linux_power_supply
diff options
context:
space:
mode:
Diffstat (limited to 'collectors/python.d.plugin/linux_power_supply')
-rw-r--r--collectors/python.d.plugin/linux_power_supply/Makefile.inc13
-rw-r--r--collectors/python.d.plugin/linux_power_supply/README.md67
-rw-r--r--collectors/python.d.plugin/linux_power_supply/linux_power_supply.chart.py160
-rw-r--r--collectors/python.d.plugin/linux_power_supply/linux_power_supply.conf81
4 files changed, 321 insertions, 0 deletions
diff --git a/collectors/python.d.plugin/linux_power_supply/Makefile.inc b/collectors/python.d.plugin/linux_power_supply/Makefile.inc
new file mode 100644
index 000000000..1864ba524
--- /dev/null
+++ b/collectors/python.d.plugin/linux_power_supply/Makefile.inc
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# THIS IS NOT A COMPLETE Makefile
+# IT IS INCLUDED BY ITS PARENT'S Makefile.am
+# IT IS REQUIRED TO REFERENCE ALL FILES RELATIVE TO THE PARENT
+
+# install these files
+dist_python_DATA += linux_power_supply/linux_power_supply.chart.py
+dist_pythonconfig_DATA += linux_power_supply/linux_power_supply.conf
+
+# do not install these files, but include them in the distribution
+dist_noinst_DATA += linux_power_supply/README.md linux_power_supply/Makefile.inc
+
diff --git a/collectors/python.d.plugin/linux_power_supply/README.md b/collectors/python.d.plugin/linux_power_supply/README.md
new file mode 100644
index 000000000..5cfbe41ce
--- /dev/null
+++ b/collectors/python.d.plugin/linux_power_supply/README.md
@@ -0,0 +1,67 @@
+# linux\_power\_supply
+
+This module monitors variosu metrics reported by power supply drivers
+on Linux. This allows tracking and alerting on things like remaining
+battery capacity.
+
+Depending on the uderlying driver, it may provide the following charts
+and metrics:
+
+1. Capacity: The power supply capacity expressed as a percentage.
+ * capacity\_now
+
+2. Charge: The charge for the power supply, expressed as microamphours.
+ * charge\_full\_design
+ * charge\_full
+ * charge\_now
+ * charge\_empty
+ * charge\_empty\_design
+
+3. Energy: The energy for the power supply, expressed as microwatthours.
+ * energy\_full\_design
+ * energy\_full
+ * energy\_now
+ * energy\_empty
+ * energy\_empty\_design
+
+2. Voltage: The voltage for the power supply, expressed as microvolts.
+ * voltage\_max\_design
+ * voltage\_max
+ * voltage\_now
+ * voltage\_min
+ * voltage\_min\_design
+
+### configuration
+
+Sample:
+
+```yaml
+battery:
+ supply: 'BAT0'
+ charts: 'capacity charge energy voltage'
+```
+
+The `supply` key specifies the name of the power supply device to monitor.
+You can use `ls /sys/class/power_supply` to get a list of such devices
+on your system.
+
+The `charts` key is a space separated list of which charts to try
+to display. It defaults to trying to display everything.
+
+### notes
+
+* Most drivers provide at least the first chart. Battery powered ACPI
+compliant systems (like most laptops) provide all but the third, but do
+not provide all of the metrics for each chart.
+
+* Current, energy, and voltages are reported with a _very_ high precision
+by the power\_supply framework. Usually, this is far higher than the
+actual hardware supports reporting, so expect to see changes in these
+charts jump instead of scaling smoothly.
+
+* If `max` or `full` attribute is defined by the driver, but not a
+corresponding `min or `empty` attribute, then netdata will still provide
+the corresponding `min` or `empty`, which will then always read as zero.
+This way, alerts which match on these will still work.
+
+---
diff --git a/collectors/python.d.plugin/linux_power_supply/linux_power_supply.chart.py b/collectors/python.d.plugin/linux_power_supply/linux_power_supply.chart.py
new file mode 100644
index 000000000..71d834e5d
--- /dev/null
+++ b/collectors/python.d.plugin/linux_power_supply/linux_power_supply.chart.py
@@ -0,0 +1,160 @@
+# -*- coding: utf-8 -*-
+# Description: Linux power_supply netdata python.d module
+# Author: Austin S. Hemmelgarn (Ferroin)
+
+import os
+import platform
+
+from bases.FrameworkServices.SimpleService import SimpleService
+
+# Everything except percentages is reported as µ units.
+PRECISION = 10 ** 6
+
+# A priority of 90000 places us next to the other PSU related stuff.
+PRIORITY = 90000
+
+# We add our charts dynamically when we probe for the device attributes,
+# so these are empty by default.
+ORDER = []
+
+CHARTS = {}
+
+
+def get_capacity_chart(syspath):
+ # Capacity is measured in percent. We track one value.
+ options = [None, 'Capacity', '%', 'power_supply', 'power_supply.capacity', 'line']
+ lines = list()
+ attr_now = 'capacity'
+ if get_sysfs_value(os.path.join(syspath, attr_now)) is not None:
+ lines.append([attr_now, attr_now, 'absolute', 1, 1])
+ return {'capacity': {'options': options, 'lines': lines}}, [attr_now]
+ else:
+ return None, None
+
+
+def get_generic_chart(syspath, name, unit, maxname, minname):
+ # Used to generate charts for energy, charge, and voltage.
+ options = [None, name.title(), unit, 'power_supply', 'power_supply.{0}'.format(name), 'line']
+ lines = list()
+ attrlist = list()
+ attr_max_design = '{0}_{1}_design'.format(name, maxname)
+ attr_max = '{0}_{1}'.format(name, maxname)
+ attr_now = '{0}_now'.format(name)
+ attr_min = '{0}_{1}'.format(name, minname)
+ attr_min_design = '{0}_{1}_design'.format(name, minname)
+ if get_sysfs_value(os.path.join(syspath, attr_now)) is not None:
+ lines.append([attr_now, attr_now, 'absolute', 1, PRECISION])
+ attrlist.append(attr_now)
+ else:
+ return None, None
+ if get_sysfs_value(os.path.join(syspath, attr_max)) is not None:
+ lines.insert(0, [attr_max, attr_max, 'absolute', 1, PRECISION])
+ lines.append([attr_min, attr_min, 'absolute', 1, PRECISION])
+ attrlist.append(attr_max)
+ attrlist.append(attr_min)
+ elif get_sysfs_value(os.path.join(syspath, attr_min)) is not None:
+ lines.append([attr_min, attr_min, 'absolute', 1, PRECISION])
+ attrlist.append(attr_min)
+ if get_sysfs_value(os.path.join(syspath, attr_max_design)) is not None:
+ lines.insert(0, [attr_max_design, attr_max_design, 'absolute', 1, PRECISION])
+ lines.append([attr_min_design, attr_min_design, 'absolute', 1, PRECISION])
+ attrlist.append(attr_max_design)
+ attrlist.append(attr_min_design)
+ elif get_sysfs_value(os.path.join(syspath, attr_min_design)) is not None:
+ lines.append([attr_min_design, attr_min_design, 'absolute', 1, PRECISION])
+ attrlist.append(attr_min_design)
+ return {name: {'options': options, 'lines': lines}}, attrlist
+
+
+def get_charge_chart(syspath):
+ # Charge is measured in microamphours. We track up to five
+ # attributes.
+ return get_generic_chart(syspath, 'charge', 'µAh', 'full', 'empty')
+
+
+def get_energy_chart(syspath):
+ # Energy is measured in microwatthours. We track up to five
+ # attributes.
+ return get_generic_chart(syspath, 'energy', 'µWh', 'full', 'empty')
+
+
+def get_voltage_chart(syspath):
+ # Voltage is measured in microvolts. We track up to five attributes.
+ return get_generic_chart(syspath, 'voltage', 'µV', 'min', 'max')
+
+
+# This is a list of functions for generating charts. Used below to save
+# a bit of code (and to make it a bit easier to add new charts).
+GET_CHART = {
+ 'capacity': get_capacity_chart,
+ 'charge': get_charge_chart,
+ 'energy': get_energy_chart,
+ 'voltage': get_voltage_chart
+}
+
+
+# This opens the specified file and returns the value in it or None if
+# the file doesn't exist.
+def get_sysfs_value(filepath):
+ try:
+ with open(filepath, 'r') as datasource:
+ return int(datasource.read())
+ except (OSError, IOError):
+ return None
+
+
+class Service(SimpleService):
+ def __init__(self, configuration=None, name=None):
+ SimpleService.__init__(self, configuration=configuration, name=name)
+ self.definitions = dict()
+ self.order = list()
+ self.attrlist = list()
+ self.supply = self.configuration.get('supply', None)
+ if self.supply is not None:
+ self.syspath = '/sys/class/power_supply/{0}'.format(self.supply)
+ self.types = self.configuration.get('charts', 'capacity').split()
+
+ def check(self):
+ if platform.system() != 'Linux':
+ self.error('Only supported on Linux.')
+ return False
+ if self.supply is None:
+ self.error('No power supply specified for monitoring.')
+ return False
+ if not self.types:
+ self.error('No attributes requested for monitoring.')
+ return False
+ if not os.access(self.syspath, os.R_OK):
+ self.error('Unable to access {0}'.format(self.syspath))
+ return False
+ return self.create_charts()
+
+ def create_charts(self):
+ chartset = set(GET_CHART).intersection(set(self.types))
+ if not chartset:
+ self.error('No valid attributes requested for monitoring.')
+ return False
+ charts = dict()
+ attrlist = list()
+ for item in chartset:
+ chart, attrs = GET_CHART[item](self.syspath)
+ if chart is not None:
+ charts.update(chart)
+ attrlist.extend(attrs)
+ if len(charts) == 0:
+ self.error('No charts can be created.')
+ return False
+ self.definitions.update(charts)
+ self.order.extend(sorted(charts))
+ self.attrlist.extend(attrlist)
+ return True
+
+ def _get_data(self):
+ data = dict()
+ for attr in self.attrlist:
+ attrpath = os.path.join(self.syspath, attr)
+ if attr.endswith(('_min', '_min_design', '_empty', '_empty_design')):
+ data[attr] = get_sysfs_value(attrpath) or 0
+ else:
+ data[attr] = get_sysfs_value(attrpath)
+ return data
diff --git a/collectors/python.d.plugin/linux_power_supply/linux_power_supply.conf b/collectors/python.d.plugin/linux_power_supply/linux_power_supply.conf
new file mode 100644
index 000000000..3cb610f7f
--- /dev/null
+++ b/collectors/python.d.plugin/linux_power_supply/linux_power_supply.conf
@@ -0,0 +1,81 @@
+# netdata python.d.plugin configuration for linux_power_supply
+#
+# This file is in YaML format. Generally the format is:
+#
+# name: value
+#
+# There are 2 sections:
+# - global variables
+# - one or more JOBS
+#
+# JOBS allow you to collect values from multiple sources.
+# Each source will have its own set of charts.
+#
+# JOB parameters have to be indented (using spaces only, example below).
+
+# ----------------------------------------------------------------------
+# 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_everye
+# 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
+#
+# In addition to the above parameters, linux_power_supply also supports
+# the following extra parameters.
+#
+# supply: '' # the name of the power supply to monitor
+# charts: 'capacity' # a space separated list of the charts to try
+# # and generate valid charts are 'capacity',
+# # 'charge', 'current', and 'voltage'
+#
+# Note that linux_power_supply will not automatically detect power
+# supplies in the system, you have to manually specify which ones you
+# want it to monitor.
+#
+# The following config will work to monitor the first battery in most
+# ACPI compliant battery powered systems (such as most laptops).
+#
+# battery:
+# name: battery
+# supply: BAT0