python.d.plugin
is a Netdata external plugin. It is an orchestrator for data collection modules written in python
.
- It runs as an independent process
ps fax
shows it - It is started and stopped automatically by Netdata
- It communicates with Netdata via a unidirectional pipe (sending data to the
netdata
daemon) - Supports any number of data collection modules
- Allows each module to have one or more data collection jobs
- Each job is collecting one or more metrics from a single data source
All third party libraries should be installed system-wide or in python_modules
directory.
Module configurations are written in YAML and pyYAML is required.
Every configuration file must have one of two formats:
- Configuration for only one job:
update_every : 2 # update frequency
priority : 20000 # where it is shown on dashboard
other_var1 : bla # variables passed to module
other_var2 : alb
- Configuration for many jobs (ex. mysql):
# module defaults:
update_every : 2
priority : 20000
local: # job name
update_every : 5 # job update frequency
other_var1 : some_val # module specific variable
other_job:
priority : 5 # job position on dashboard
other_var2 : val # module specific variable
update_every
and priority
are always optional.
# become user netdata
sudo su -s /bin/bash netdata
Depending on where Netdata was installed, execute one of the following commands to trace the execution of a python module:
# execute the plugin in debug mode, for a specific module
/opt/netdata/usr/libexec/netdata/plugins.d/python.d.plugin <module> debug trace
/usr/libexec/netdata/plugins.d/python.d.plugin <module> debug trace
Where [module]
is the directory name under https://github.com/netdata/netdata/tree/master/collectors/python.d.plugin
Note: If you would like execute a collector in debug mode while it is still running by Netdata, you can pass the nolock
CLI option to the above commands.
Writing new python module is simple. You just need to remember to include 5 major things:
- ORDER global list
- CHART global dictionary
- Service class
- _get_data method
If you plan to submit the module in a PR, make sure and go through the PR checklist for new modules beforehand to make sure you have updated all the files you need to.
For a quick start, you can look at the example plugin.
Note: If you are working 'locally' on a new collector and would like to run it in an already installed and running
Netdata (as opposed to having to install Netdata from source again with your new changes) to can copy over the relevant
file to where Netdata expects it and then either sudo systemctl restart netdata
to have it be picked up and used by
Netdata or you can just run the updated collector in debug mode by following a process like below (this assumes you have
installed Netdata from a GitHub fork you
have made to do your development on).
# clone your fork (done once at the start but shown here for clarity)
#git clone --branch my-example-collector https://github.com/mygithubusername/netdata.git --depth=100 --recursive
# go into your netdata source folder
cd netdata
# git pull your latest changes (assuming you built from a fork you are using to develop on)
git pull
# instead of running the installer we can just copy over the updated collector files
#sudo ./netdata-installer.sh --dont-wait
# copy over the file you have updated locally (pretending we are working on the 'example' collector)
sudo cp collectors/python.d.plugin/example/example.chart.py /usr/libexec/netdata/python.d/
# become user netdata
sudo su -s /bin/bash netdata
# run your updated collector in debug mode to see if it works without having to reinstall netdata
/usr/libexec/netdata/plugins.d/python.d.plugin example debug trace nolock
ORDER
list should contain the order of chart ids. Example:
ORDER = ['first_chart', 'second_chart', 'third_chart']
CHART
dictionary is a little bit trickier. It should contain the chart definition in following format:
CHART = {
id: {
'options': [name, title, units, family, context, charttype],
'lines': [
[unique_dimension_name, name, algorithm, multiplier, divisor]
]}
All names are better explained in the External Plugins section.
Parameters like priority
and update_every
are handled by python.d.plugin
.
Every module needs to implement its own Service
class. This class should inherit from one of the framework classes:
SimpleService
UrlService
SocketService
LogService
ExecutableService
Also it needs to invoke the parent class constructor in a specific way as well as assign global variables to class variables.
Simple example:
from base import UrlService
class Service(UrlService):
def __init__(self, configuration=None, name=None):
UrlService.__init__(self, configuration=configuration, name=name)
self.order = ORDER
self.definitions = CHARTS
This method should grab raw data from _get_raw_data
, parse it, and return a dictionary where keys are unique dimension names or None
if no data is collected.
Example:
def _get_data(self):
try:
raw = self._get_raw_data().split(" ")
return {'active': int(raw[2])}
except (ValueError, AttributeError):
return None
Every framework class has some user-configurable variables which are specific to this particular class. Those variables should have default values initialized in the child class constructor.
If module needs some additional user-configurable variable, it can be accessed from the self.configuration
list and assigned in constructor or custom check
method. Example:
def __init__(self, configuration=None, name=None):
UrlService.__init__(self, configuration=configuration, name=name)
try:
self.baseurl = str(self.configuration['baseurl'])
except (KeyError, TypeError):
self.baseurl = "http://localhost:5001"
Classes implement _get_raw_data
which should be used to grab raw data. This method usually returns a list of strings.
This is last resort class, if a new module cannot be written by using other framework class this one can be used.
Example: ceph
, sensors
It is the lowest-level class which implements most of module logic, like:
- threading
- handling run times
- chart formatting
- logging
- chart creation and updating
Examples: apache_cache
, nginx_log
Variable from config file: log_path
.
Object created from this class reads new lines from file specified in log_path
variable. It will check if file exists and is readable. Also _get_raw_data
returns list of strings where each string is one line from file specified in log_path
.
Examples: exim
, postfix
Variable from config file: command
.
This allows to execute a shell command in a secure way. It will check for invalid characters in command
variable and won't proceed if there is one of:
- '&'
- '|'
- ';'
- '>'
- '<'
For additional security it uses python subprocess.Popen
(without shell=True
option) to execute command. Command can be specified with absolute or relative name. When using relative name, it will try to find command
in PATH
environment variable as well as in /sbin
and /usr/sbin
.
_get_raw_data
returns list of decoded lines returned by command
.
Examples: apache
, nginx
, tomcat
_Multiple Endpoints (urls) Examples: rabbitmq
(simpler).
Variables from config file: url
, user
, pass
.
If data is grabbed by accessing service via HTTP protocol, this class can be used. It can handle HTTP Basic Auth when specified with user
and pass
credentials.
Please note that the config file can use different variables according to the specification of each module.
_get_raw_data
returns list of utf-8 decoded strings (lines).
Examples: dovecot
, redis
Variables from config file: unix_socket
, host
, port
, request
.
Object will try execute request
using either unix_socket
or TCP/IP socket with combination of host
and port
. This can access unix sockets with SOCK_STREAM or SOCK_DGRAM protocols and TCP/IP sockets in version 4 and 6 with SOCK_STREAM setting.
Sockets are accessed in non-blocking mode with 15 second timeout.
After every execution of _get_raw_data
socket is closed, to prevent this module needs to set _keep_alive
variable to True
and implement custom _check_raw_data
method.
_check_raw_data
should take raw data and return True
if all data is received otherwise it should return False
. Also it should do it in fast and efficient way.
This is a generic checklist for submitting a new Python plugin for Netdata. It is by no means comprehensive.
At minimum, to be buildable and testable, the PR needs to include:
- The module itself, following proper naming conventions:
collectors/python.d.plugin/<module_dir>/<module_name>.chart.py
- A README.md file for the plugin under
collectors/python.d.plugin/<module_dir>
. - The configuration file for the module:
collectors/python.d.plugin/<module_dir>/<module_name>.conf
. Python config files are in YAML format, and should include comments describing what options are present. The instructions are also needed in the configuration section of the README.md - A basic configuration for the plugin in the appropriate global config file:
collectors/python.d.plugin/python.d.conf
, which is also in YAML format. Either add a line that reads# <module_name>: yes
if the module is to be enabled by default, or one that reads<module_name>: no
if it is to be disabled by default. - A makefile for the plugin at
collectors/python.d.plugin/<module_dir>/Makefile.inc
. Check an existing plugin for what this should look like. - A line in
collectors/python.d.plugin/Makefile.am
including the above-mentioned makefile. Place it with the other plugin includes (please keep the includes sorted alphabetically). - Optionally, chart information in
web/gui/dashboard_info.js
. This generally involves specifying a name and icon for the section, and may include descriptions for the section or individual charts. - Optionally, some default alarm configurations for your collector in
health/health.d/<module_name>.conf
and a line adding<module_name>.conf
inhealth/Makefile.am
.