diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 11:19:16 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 12:07:37 +0000 |
commit | b485aab7e71c1625cfc27e0f92c9509f42378458 (patch) | |
tree | ae9abe108601079d1679194de237c9a435ae5b55 /src/collectors/charts.d.plugin | |
parent | Adding upstream version 1.44.3. (diff) | |
download | netdata-b485aab7e71c1625cfc27e0f92c9509f42378458.tar.xz netdata-b485aab7e71c1625cfc27e0f92c9509f42378458.zip |
Adding upstream version 1.45.3+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/collectors/charts.d.plugin')
33 files changed, 4848 insertions, 0 deletions
diff --git a/src/collectors/charts.d.plugin/README.md b/src/collectors/charts.d.plugin/README.md new file mode 100644 index 000000000..72429d278 --- /dev/null +++ b/src/collectors/charts.d.plugin/README.md @@ -0,0 +1,190 @@ +# charts.d.plugin + +`charts.d.plugin` is a Netdata external plugin. It is an **orchestrator** for data collection modules written in `BASH` v4+. + +1. It runs as an independent process `ps fax` shows it +2. It is started and stopped automatically by Netdata +3. It communicates with Netdata via a unidirectional pipe (sending data to the `netdata` daemon) +4. Supports any number of data collection **modules** + +To better understand the guidelines and the API behind our External plugins, please have a look at the [Introduction to External plugins](https://github.com/netdata/netdata/blob/master/src/collectors/plugins.d/README.md) prior to reading this page. + + +`charts.d.plugin` has been designed so that the actual script that will do data collection will be permanently in +memory, collecting data with as little overheads as possible +(i.e. initialize once, repeatedly collect values with minimal overhead). + +`charts.d.plugin` looks for scripts in `/usr/lib/netdata/charts.d`. +The scripts should have the filename suffix: `.chart.sh`. + +By default, `charts.d.plugin` is not included as part of the install when using [our official native DEB/RPM packages](https://github.com/netdata/netdata/blob/master/packaging/installer/methods/packages.md). You can install it by installing the `netdata-plugin-chartsd` package. + +## Configuration + +`charts.d.plugin` itself can be [configured](https://github.com/netdata/netdata/blob/master/docs/configure/nodes.md#use-edit-config-to-edit-configuration-files) using the configuration file `/etc/netdata/charts.d.conf`. This file is also a BASH script. + +In this file, you can place statements like this: + +```conf +enable_all_charts="yes" +X="yes" +Y="no" +``` + +where `X` and `Y` are the names of individual charts.d collector scripts. +When set to `yes`, charts.d will evaluate the collector script (see below). +When set to `no`, charts.d will ignore the collector script. + +The variable `enable_all_charts` sets the default enable/disable state for all charts. + +## A charts.d module + +A `charts.d.plugin` module is a BASH script defining a few functions. + +For a module called `X`, the following criteria must be met: + +1. The module script must be called `X.chart.sh` and placed in `/usr/libexec/netdata/charts.d`. + +2. If the module needs a configuration, it should be called `X.conf` and placed in `/etc/netdata/charts.d`. + The configuration file `X.conf` is also a BASH script itself. + You can edit the default files supplied by Netdata, by editing `/etc/netdata/edit-config charts.d/X.conf`, where `X` is the name of the module. + +3. All functions and global variables defined in the script and its configuration, must begin with `X_`. + +4. The following functions must be defined: + + - `X_check()` - returns 0 or 1 depending on whether the module is able to run or not + (following the standard Linux command line return codes: 0 = OK, the collector can operate and 1 = FAILED, + the collector cannot be used). + + - `X_create()` - creates the Netdata charts (commands `CHART` and `DIMENSION`). + The return value does matter: 0 = OK, 1 = FAILED. + + - `X_update()` - collects the values for the defined charts (commands `BEGIN`, `SET`, `END`). + The return value also matters: 0 = OK, 1 = FAILED. + +5. The following global variables are available to be set: + - `X_update_every` - is the data collection frequency for the module script, in seconds. + +The module script may use more functions or variables. But all of them must begin with `X_`. + +### X_check() + +The purpose of the BASH function `X_check()` is to check if the module can collect data (or check its config). + +For example, if the module is about monitoring a local mysql database, the `X_check()` function may attempt to +connect to a local mysql database to find out if it can read the values it needs. + +`X_check()` is run only once for the lifetime of the module. + +### X_create() + +The purpose of the BASH function `X_create()` is to create the charts and dimensions using the standard Netdata +plugin guidelines. + +`X_create()` will be called just once and only after `X_check()` was successful. +You can however call it yourself when there is need for it (for example to add a new dimension to an existing chart). + +A non-zero return value will disable the collector. + +### X_update() + +`X_update()` will be called repeatedly every `X_update_every` seconds, to collect new values and send them to Netdata, +following the Netdata plugin guidelines. + +The function will be called with one parameter: microseconds since the last time it was run. This value should be +appended to the `BEGIN` statement of every chart updated by the collector script. + +A non-zero return value will disable the collector. + +### Useful functions charts.d provides + +Module scripts can use the following charts.d functions: + +#### require_cmd command + +`require_cmd()` will check if a command is available in the running system. + +For example, your `X_check()` function may use it like this: + +```sh +mysql_check() { + require_cmd mysql || return 1 + return 0 +} +``` + +Using the above, if the command `mysql` is not available in the system, the `mysql` module will be disabled. + +#### fixid "string" + +`fixid()` will get a string and return a properly formatted id for a chart or dimension. + +This is an expensive function that should not be used in `X_update()`. +You can keep the generated id in a BASH associative array to have the values availables in `X_update()`, like this: + +```sh +declare -A X_ids=() +X_create() { + local name="a very bad name for id" + + X_ids[$name]="$(fixid "$name")" +} + +X_update() { + local microseconds="$1" + + ... + local name="a very bad name for id" + ... + + echo "BEGIN ${X_ids[$name]} $microseconds" + ... +} +``` + +### Debugging your collectors + +You can run `charts.d.plugin` by hand with something like this: + +```sh +# become user netdata +sudo su -s /bin/sh netdata + +# run the plugin in debug mode +/usr/libexec/netdata/plugins.d/charts.d.plugin debug 1 X Y Z +``` + +Charts.d will run in `debug` mode, with an update frequency of `1`, evaluating only the collector scripts +`X`, `Y` and `Z`. You can define zero or more module scripts. If none is defined, charts.d will evaluate all +module scripts available. + +Keep in mind that if your configs are not in `/etc/netdata`, you should do the following before running +`charts.d.plugin`: + +```sh +export NETDATA_USER_CONFIG_DIR="/path/to/etc/netdata" +``` + +Also, remember that Netdata runs `chart.d.plugin` as user `netdata` (or any other user the `netdata` process is configured to run as). + +## Running multiple instances of charts.d.plugin + +`charts.d.plugin` will call the `X_update()` function one after another. This means that a delay in collector `X` +will also delay the collection of `Y` and `Z`. + +You can have multiple `charts.d.plugin` running to overcome this problem. + +This is what you need to do: + +1. Decide a new name for the new charts.d instance: example `charts2.d`. + +2. Create/edit the files `/etc/netdata/charts.d.conf` and `/etc/netdata/charts2.d.conf` and enable / disable the + module you want each to run. Remember to set `enable_all_charts="no"` to both of them, and enable the individual + modules for each. + +3. link `/usr/libexec/netdata/plugins.d/charts.d.plugin` to `/usr/libexec/netdata/plugins.d/charts2.d.plugin`. + Netdata will spawn a new charts.d process. + +Execute the above in this order, since Netdata will (by default) attempt to start new plugins soon after they are +created in `/usr/libexec/netdata/plugins.d/`. diff --git a/src/collectors/charts.d.plugin/ap/README.md b/src/collectors/charts.d.plugin/ap/README.md new file mode 120000 index 000000000..5b6e75130 --- /dev/null +++ b/src/collectors/charts.d.plugin/ap/README.md @@ -0,0 +1 @@ +integrations/access_points.md
\ No newline at end of file diff --git a/src/collectors/charts.d.plugin/ap/ap.chart.sh b/src/collectors/charts.d.plugin/ap/ap.chart.sh new file mode 100644 index 000000000..80c9dc602 --- /dev/null +++ b/src/collectors/charts.d.plugin/ap/ap.chart.sh @@ -0,0 +1,179 @@ +# shellcheck shell=bash +# no need for shebang - this file is loaded from charts.d.plugin +# SPDX-License-Identifier: GPL-3.0-or-later + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# + +# _update_every is a special variable - it holds the number of seconds +# between the calls of the _update() function +ap_update_every= +ap_priority=6900 + +declare -A ap_devs=() + +# _check is called once, to find out if this chart should be enabled or not +ap_check() { + require_cmd iw || return 1 + local ev + ev=$(run iw dev | awk ' + BEGIN { + i = ""; + ssid = ""; + ap = 0; + } + /^[ \t]+Interface / { + if( ap == 1 ) { + print "ap_devs[" i "]=\"" ssid "\"" + } + + i = $2; + ssid = ""; + ap = 0; + } + /^[ \t]+ssid / { ssid = $2; } + /^[ \t]+type AP$/ { ap = 1; } + END { + if( ap == 1 ) { + print "ap_devs[" i "]=\"" ssid "\"" + } + } + ') + eval "${ev}" + + # this should return: + # - 0 to enable the chart + # - 1 to disable the chart + + [ ${#ap_devs[@]} -gt 0 ] && return 0 + error "no devices found in AP mode, with 'iw dev'" + return 1 +} + +# _create is called once, to create the charts +ap_create() { + local ssid dev + + for dev in "${!ap_devs[@]}"; do + ssid="${ap_devs[${dev}]}" + + # create the chart with 3 dimensions + cat << EOF +CHART ap_clients.${dev} '' "Connected clients to ${ssid} on ${dev}" "clients" ${dev} ap.clients line $((ap_priority + 1)) $ap_update_every '' '' 'ap' +DIMENSION clients '' absolute 1 1 + +CHART ap_bandwidth.${dev} '' "Bandwidth for ${ssid} on ${dev}" "kilobits/s" ${dev} ap.net area $((ap_priority + 2)) $ap_update_every '' '' 'ap' +DIMENSION received '' incremental 8 1024 +DIMENSION sent '' incremental -8 1024 + +CHART ap_packets.${dev} '' "Packets for ${ssid} on ${dev}" "packets/s" ${dev} ap.packets line $((ap_priority + 3)) $ap_update_every '' '' 'ap' +DIMENSION received '' incremental 1 1 +DIMENSION sent '' incremental -1 1 + +CHART ap_issues.${dev} '' "Transmit Issues for ${ssid} on ${dev}" "issues/s" ${dev} ap.issues line $((ap_priority + 4)) $ap_update_every '' '' 'ap' +DIMENSION retries 'tx retries' incremental 1 1 +DIMENSION failures 'tx failures' incremental -1 1 + +CHART ap_signal.${dev} '' "Average Signal for ${ssid} on ${dev}" "dBm" ${dev} ap.signal line $((ap_priority + 5)) $ap_update_every '' '' 'ap' +DIMENSION signal 'average signal' absolute 1 1000 + +CHART ap_bitrate.${dev} '' "Bitrate for ${ssid} on ${dev}" "Mbps" ${dev} ap.bitrate line $((ap_priority + 6)) $ap_update_every '' '' 'ap' +DIMENSION receive '' absolute 1 1000 +DIMENSION transmit '' absolute -1 1000 +DIMENSION expected 'expected throughput' absolute 1 1000 +EOF + done + + return 0 +} + +# _update is called continuously, to collect the values +ap_update() { + # the first argument to this function is the microseconds since last update + # pass this parameter to the BEGIN statement (see below). + + # do all the work to collect / calculate the values + # for each dimension + # remember: KEEP IT SIMPLE AND SHORT + + for dev in "${!ap_devs[@]}"; do + echo + echo "DEVICE ${dev}" + iw "${dev}" station dump + done | awk ' + function zero_data() { + dev = ""; + c = 0; + rb = 0; + tb = 0; + rp = 0; + tp = 0; + tr = 0; + tf = 0; + tt = 0; + rt = 0; + s = 0; + g = 0; + e = 0; + } + function print_device() { + if(dev != "" && length(dev) > 0) { + print "BEGIN ap_clients." dev; + print "SET clients = " c; + print "END"; + print "BEGIN ap_bandwidth." dev; + print "SET received = " rb; + print "SET sent = " tb; + print "END"; + print "BEGIN ap_packets." dev; + print "SET received = " rp; + print "SET sent = " tp; + print "END"; + print "BEGIN ap_issues." dev; + print "SET retries = " tr; + print "SET failures = " tf; + print "END"; + + if( c == 0 ) c = 1; + print "BEGIN ap_signal." dev; + print "SET signal = " int(s / c); + print "END"; + print "BEGIN ap_bitrate." dev; + print "SET receive = " int(rt / c); + print "SET transmit = " int(tt / c); + print "SET expected = " int(e / c); + print "END"; + } + zero_data(); + } + BEGIN { + zero_data(); + } + /^DEVICE / { + print_device(); + dev = $2; + } + /^Station/ { c++; } + /^[ \t]+rx bytes:/ { rb += $3; } + /^[ \t]+tx bytes:/ { tb += $3; } + /^[ \t]+rx packets:/ { rp += $3; } + /^[ \t]+tx packets:/ { tp += $3; } + /^[ \t]+tx retries:/ { tr += $3; } + /^[ \t]+tx failed:/ { tf += $3; } + /^[ \t]+signal:/ { x = $2; s += x * 1000; } + /^[ \t]+rx bitrate:/ { x = $3; rt += x * 1000; } + /^[ \t]+tx bitrate:/ { x = $3; tt += x * 1000; } + /^[ \t]+expected throughput:(.*)Mbps/ { + x=$3; + sub(/Mbps/, "", x); + e += x * 1000; + } + END { + print_device(); + } + ' + + return 0 +} diff --git a/src/collectors/charts.d.plugin/ap/ap.conf b/src/collectors/charts.d.plugin/ap/ap.conf new file mode 100644 index 000000000..38fc157ce --- /dev/null +++ b/src/collectors/charts.d.plugin/ap/ap.conf @@ -0,0 +1,23 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2018 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +# nothing fancy to configure. +# this module will run +# iw dev - to find wireless devices in AP mode +# iw ${dev} station dump - to get connected clients +# based on the above, it generates several charts + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#ap_update_every= + +# the charts priority on the dashboard +#ap_priority=6900 + +# the number of retries to do in case of failure +# before disabling the module +#ap_retries=10 diff --git a/src/collectors/charts.d.plugin/ap/integrations/access_points.md b/src/collectors/charts.d.plugin/ap/integrations/access_points.md new file mode 100644 index 000000000..0c0a3feb6 --- /dev/null +++ b/src/collectors/charts.d.plugin/ap/integrations/access_points.md @@ -0,0 +1,174 @@ +<!--startmeta +custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/collectors/charts.d.plugin/ap/README.md" +meta_yaml: "https://github.com/netdata/netdata/edit/master/src/collectors/charts.d.plugin/ap/metadata.yaml" +sidebar_label: "Access Points" +learn_status: "Published" +learn_rel_path: "Collecting Metrics/Linux Systems/Network" +most_popular: False +message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE" +endmeta--> + +# Access Points + + +<img src="https://netdata.cloud/img/network-wired.svg" width="150"/> + + +Plugin: charts.d.plugin +Module: ap + +<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" /> + +## Overview + +The ap collector visualizes data related to wireless access points. + +It uses the `iw` command line utility to detect access points. For each interface that is of `type AP`, it then runs `iw INTERFACE station dump` and collects statistics. + +This collector is only supported on the following platforms: + +- Linux + +This collector only supports collecting metrics from a single instance of this integration. + + +### Default Behavior + +#### Auto-Detection + +The plugin is able to auto-detect if you are running access points on your linux box. + +#### Limits + +The default configuration for this integration does not impose any limits on data collection. + +#### Performance Impact + +The default configuration for this integration is not expected to impose a significant performance impact on the system. + + +## Metrics + +Metrics grouped by *scope*. + +The scope defines the instance that the metric belongs to. An instance is uniquely identified by a set of labels. + + + +### Per wireless device + +These metrics refer to the entire monitored application. + +This scope has no labels. + +Metrics: + +| Metric | Dimensions | Unit | +|:------|:----------|:----| +| ap.clients | clients | clients | +| ap.net | received, sent | kilobits/s | +| ap.packets | received, sent | packets/s | +| ap.issues | retries, failures | issues/s | +| ap.signal | average signal | dBm | +| ap.bitrate | receive, transmit, expected | Mbps | + + + +## Alerts + +There are no alerts configured by default for this integration. + + +## Setup + +### Prerequisites + +#### Install charts.d plugin + +If [using our official native DEB/RPM packages](https://github.com/netdata/netdata/blob/master/packaging/installer/UPDATE.md#determine-which-installation-method-you-used), make sure `netdata-plugin-chartsd` is installed. + + +#### `iw` utility. + +Make sure the `iw` utility is installed. + + +### Configuration + +#### File + +The configuration file name for this integration is `charts.d/ap.conf`. + + +You can edit the configuration file using the `edit-config` script from the +Netdata [config directory](https://github.com/netdata/netdata/blob/master/docs/netdata-agent/configuration.md#the-netdata-config-directory). + +```bash +cd /etc/netdata 2>/dev/null || cd /opt/netdata/etc/netdata +sudo ./edit-config charts.d/ap.conf +``` +#### Options + +The config file is sourced by the charts.d plugin. It's a standard bash file. + +The following collapsed table contains all the options that can be configured for the ap collector. + + +<details><summary>Config options</summary> + +| Name | Description | Default | Required | +|:----|:-----------|:-------|:--------:| +| ap_update_every | The data collection frequency. If unset, will inherit the netdata update frequency. | 1 | no | +| ap_priority | Controls the order of charts at the netdata dashboard. | 6900 | no | +| ap_retries | The number of retries to do in case of failure before disabling the collector. | 10 | no | + +</details> + +#### Examples + +##### Change the collection frequency + +Specify a custom collection frequence (update_every) for this collector + +```yaml +# the data collection frequency +# if unset, will inherit the netdata update frequency +ap_update_every=10 + +# the charts priority on the dashboard +#ap_priority=6900 + +# the number of retries to do in case of failure +# before disabling the module +#ap_retries=10 + +``` + + +## Troubleshooting + +### Debug Mode + +To troubleshoot issues with the `ap` collector, run the `charts.d.plugin` with the debug option enabled. The output +should give you clues as to why the collector isn't working. + +- Navigate to the `plugins.d` directory, usually at `/usr/libexec/netdata/plugins.d/`. If that's not the case on + your system, open `netdata.conf` and look for the `plugins` setting under `[directories]`. + + ```bash + cd /usr/libexec/netdata/plugins.d/ + ``` + +- Switch to the `netdata` user. + + ```bash + sudo -u netdata -s + ``` + +- Run the `charts.d.plugin` to debug the collector: + + ```bash + ./charts.d.plugin debug 1 ap + ``` + + diff --git a/src/collectors/charts.d.plugin/ap/metadata.yaml b/src/collectors/charts.d.plugin/ap/metadata.yaml new file mode 100644 index 000000000..ee941e417 --- /dev/null +++ b/src/collectors/charts.d.plugin/ap/metadata.yaml @@ -0,0 +1,146 @@ +plugin_name: charts.d.plugin +modules: + - meta: + plugin_name: charts.d.plugin + module_name: ap + monitored_instance: + name: Access Points + link: "https://learn.netdata.cloud/docs/data-collection/networking-stack-and-network-interfaces/linux-access-points" + categories: + - data-collection.linux-systems.network-metrics + icon_filename: "network-wired.svg" + related_resources: + integrations: + list: [] + info_provided_to_referring_integrations: + description: "" + keywords: + - ap + - access + - point + - wireless + - network + most_popular: false + overview: + data_collection: + metrics_description: "The ap collector visualizes data related to wireless access points." + method_description: "It uses the `iw` command line utility to detect access points. For each interface that is of `type AP`, it then runs `iw INTERFACE station dump` and collects statistics." + supported_platforms: + include: [Linux] + exclude: [] + multi_instance: false + additional_permissions: + description: "" + default_behavior: + auto_detection: + description: "The plugin is able to auto-detect if you are running access points on your linux box." + limits: + description: "" + performance_impact: + description: "" + setup: + prerequisites: + list: + - title: "Install charts.d plugin" + description: | + If [using our official native DEB/RPM packages](https://github.com/netdata/netdata/blob/master/packaging/installer/UPDATE.md#determine-which-installation-method-you-used), make sure `netdata-plugin-chartsd` is installed. + - title: "`iw` utility." + description: "Make sure the `iw` utility is installed." + configuration: + file: + name: charts.d/ap.conf + options: + description: | + The config file is sourced by the charts.d plugin. It's a standard bash file. + + The following collapsed table contains all the options that can be configured for the ap collector. + folding: + title: "Config options" + enabled: true + list: + - name: ap_update_every + description: The data collection frequency. If unset, will inherit the netdata update frequency. + default_value: 1 + required: false + - name: ap_priority + description: Controls the order of charts at the netdata dashboard. + default_value: 6900 + required: false + - name: ap_retries + description: The number of retries to do in case of failure before disabling the collector. + default_value: 10 + required: false + examples: + folding: + enabled: false + title: "Config" + list: + - name: Change the collection frequency + description: Specify a custom collection frequence (update_every) for this collector + config: | + # the data collection frequency + # if unset, will inherit the netdata update frequency + ap_update_every=10 + + # the charts priority on the dashboard + #ap_priority=6900 + + # the number of retries to do in case of failure + # before disabling the module + #ap_retries=10 + troubleshooting: + problems: + list: [] + alerts: [] + metrics: + folding: + title: Metrics + enabled: false + description: "" + availability: [] + scopes: + - name: wireless device + description: "These metrics refer to the entire monitored application." + labels: [] + metrics: + - name: ap.clients + description: Connected clients to ${ssid} on ${dev} + unit: "clients" + chart_type: line + dimensions: + - name: clients + - name: ap.net + description: Bandwidth for ${ssid} on ${dev} + unit: "kilobits/s" + chart_type: area + dimensions: + - name: received + - name: sent + - name: ap.packets + description: Packets for ${ssid} on ${dev} + unit: "packets/s" + chart_type: line + dimensions: + - name: received + - name: sent + - name: ap.issues + description: Transmit Issues for ${ssid} on ${dev} + unit: "issues/s" + chart_type: line + dimensions: + - name: retries + - name: failures + - name: ap.signal + description: Average Signal for ${ssid} on ${dev} + unit: "dBm" + chart_type: line + dimensions: + - name: average signal + - name: ap.bitrate + description: Bitrate for ${ssid} on ${dev} + unit: "Mbps" + chart_type: line + dimensions: + - name: receive + - name: transmit + - name: expected diff --git a/src/collectors/charts.d.plugin/apcupsd/README.md b/src/collectors/charts.d.plugin/apcupsd/README.md new file mode 120000 index 000000000..fc6681fe6 --- /dev/null +++ b/src/collectors/charts.d.plugin/apcupsd/README.md @@ -0,0 +1 @@ +integrations/apc_ups.md
\ No newline at end of file diff --git a/src/collectors/charts.d.plugin/apcupsd/apcupsd.chart.sh b/src/collectors/charts.d.plugin/apcupsd/apcupsd.chart.sh new file mode 100644 index 000000000..da9cd19c3 --- /dev/null +++ b/src/collectors/charts.d.plugin/apcupsd/apcupsd.chart.sh @@ -0,0 +1,305 @@ +# shellcheck shell=bash +# no need for shebang - this file is loaded from charts.d.plugin +# SPDX-License-Identifier: GPL-3.0-or-later + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# + +apcupsd_ip= +apcupsd_port= + +declare -A apcupsd_sources=( + ["local"]="127.0.0.1:3551" +) + +# how frequently to collect UPS data +apcupsd_update_every=10 + +apcupsd_timeout=3 + +# the priority of apcupsd related to other charts +apcupsd_priority=90000 + +apcupsd_get() { + run -t $apcupsd_timeout apcaccess status "$1" +} + +is_ups_alive() { + local status + status="$(apcupsd_get "$1" | sed -e 's/STATUS.*: //' -e 't' -e 'd')" + case "$status" in + "" | "COMMLOST" | "SHUTTING DOWN") return 1 ;; + *) return 0 ;; + esac +} + +apcupsd_check() { + + # this should return: + # - 0 to enable the chart + # - 1 to disable the chart + + require_cmd apcaccess || return 1 + + # backwards compatibility + if [ "${apcupsd_ip}:${apcupsd_port}" != ":" ]; then + apcupsd_sources["local"]="${apcupsd_ip}:${apcupsd_port}" + fi + + local host working=0 failed=0 + for host in "${!apcupsd_sources[@]}"; do + apcupsd_get "${apcupsd_sources[${host}]}" >/dev/null + # shellcheck disable=2181 + if [ $? -ne 0 ]; then + error "cannot get information for apcupsd server ${host} on ${apcupsd_sources[${host}]}." + failed=$((failed + 1)) + else + if ! is_ups_alive ${apcupsd_sources[${host}]}; then + error "APC UPS ${host} on ${apcupsd_sources[${host}]} is not online." + failed=$((failed + 1)) + else + working=$((working + 1)) + fi + fi + done + + if [ ${working} -eq 0 ]; then + error "No APC UPSes found available." + return 1 + fi + + return 0 +} + +apcupsd_create() { + local host + for host in "${!apcupsd_sources[@]}"; do + # create the charts + cat <<EOF +CHART apcupsd_${host}.charge '' "UPS Charge" "percentage" ups apcupsd.charge area $((apcupsd_priority + 2)) $apcupsd_update_every '' '' 'apcupsd' +DIMENSION battery_charge charge absolute 1 100 + +CHART apcupsd_${host}.battery_voltage '' "UPS Battery Voltage" "Volts" ups apcupsd.battery.voltage line $((apcupsd_priority + 4)) $apcupsd_update_every '' '' 'apcupsd' +DIMENSION battery_voltage voltage absolute 1 100 +DIMENSION battery_voltage_nominal nominal absolute 1 100 + +CHART apcupsd_${host}.input_voltage '' "UPS Input Voltage" "Volts" input apcupsd.input.voltage line $((apcupsd_priority + 5)) $apcupsd_update_every '' '' 'apcupsd' +DIMENSION input_voltage voltage absolute 1 100 +DIMENSION input_voltage_min min absolute 1 100 +DIMENSION input_voltage_max max absolute 1 100 + +CHART apcupsd_${host}.input_frequency '' "UPS Input Frequency" "Hz" input apcupsd.input.frequency line $((apcupsd_priority + 6)) $apcupsd_update_every '' '' 'apcupsd' +DIMENSION input_frequency frequency absolute 1 100 + +CHART apcupsd_${host}.output_voltage '' "UPS Output Voltage" "Volts" output apcupsd.output.voltage line $((apcupsd_priority + 7)) $apcupsd_update_every '' '' 'apcupsd' +DIMENSION output_voltage voltage absolute 1 100 +DIMENSION output_voltage_nominal nominal absolute 1 100 + +CHART apcupsd_${host}.load '' "UPS Load" "percentage" ups apcupsd.load area $((apcupsd_priority)) $apcupsd_update_every '' '' 'apcupsd' +DIMENSION load load absolute 1 100 + +CHART apcupsd_${host}.load_usage '' "UPS Load Usage" "Watts" ups apcupsd.load_usage area $((apcupsd_priority + 1)) $apcupsd_update_every '' '' 'apcupsd' +DIMENSION load_usage load absolute 1 100 + +CHART apcupsd_${host}.temp '' "UPS Temperature" "Celsius" ups apcupsd.temperature line $((apcupsd_priority + 8)) $apcupsd_update_every '' '' 'apcupsd' +DIMENSION temp temp absolute 1 100 + +CHART apcupsd_${host}.time '' "UPS Time Remaining" "Minutes" ups apcupsd.time area $((apcupsd_priority + 3)) $apcupsd_update_every '' '' 'apcupsd' +DIMENSION time time absolute 1 100 + +CHART apcupsd_${host}.online '' "UPS ONLINE flag" "boolean" ups apcupsd.online line $((apcupsd_priority + 9)) $apcupsd_update_every '' '' 'apcupsd' +DIMENSION online online absolute 1 1 + +CHART apcupsd_${host}.selftest '' "UPS Self-Test status" "status" ups apcupsd.selftest line $((apcupsd_priority + 10)) $apcupsd_update_every '' '' 'apcupsd' +DIMENSION selftest_OK 'OK' absolute 1 1 +DIMENSION selftest_NO 'NO' absolute 1 1 +DIMENSION selftest_BT 'BT' absolute 1 1 +DIMENSION selftest_NG 'NG' absolute 1 1 + +CHART apcupsd_${host}.status '' "UPS Status" "status" ups apcupsd.status line $((apcupsd_priority + 11)) $apcupsd_update_every '' '' 'apcupsd' +DIMENSION status_ONLINE 'ONLINE' absolute 1 1 +DIMENSION status_ONBATT 'ONBATT' absolute 1 1 +DIMENSION status_OVERLOAD 'OVERLOAD' absolute 1 1 +DIMENSION status_LOWBATT 'LOWBATT' absolute 1 1 +DIMENSION status_REPLACEBATT 'REPLACEBATT' absolute 1 1 +DIMENSION status_NOBATT 'NOBATT' absolute 1 1 +DIMENSION status_SLAVE 'SLAVE' absolute 1 1 +DIMENSION status_SLAVEDOWN 'SLAVEDOWN' absolute 1 1 +DIMENSION status_COMMLOST 'COMMLOST' absolute 1 1 +DIMENSION status_CAL 'CAL' absolute 1 1 +DIMENSION status_TRIM 'TRIM' absolute 1 1 +DIMENSION status_BOOST 'BOOST' absolute 1 1 +DIMENSION status_SHUTTING_DOWN 'SHUTTING_DOWN' absolute 1 1 + +EOF + done + return 0 +} + +apcupsd_update() { + # the first argument to this function is the microseconds since last update + # pass this parameter to the BEGIN statement (see below). + + # do all the work to collect / calculate the values + # for each dimension + # remember: KEEP IT SIMPLE AND SHORT + + local host working=0 failed=0 + for host in "${!apcupsd_sources[@]}"; do + apcupsd_get "${apcupsd_sources[${host}]}" | awk " + +BEGIN { + battery_charge = 0; + battery_voltage = 0; + battery_voltage_nominal = 0; + input_voltage = 0; + input_voltage_min = 0; + input_voltage_max = 0; + input_frequency = 0; + output_voltage = 0; + output_voltage_nominal = 0; + load = 0; + temp = 0; + time = 0; + nompower = 0; + load_usage = 0; + selftest_OK = 0; + selftest_NO = 0; + selftest_BT = 0; + selftest_NG = 0; + status_ONLINE = 0; + status_CAL = 0; + status_TRIM = 0; + status_BOOST = 0; + status_ONBATT = 0; + status_OVERLOAD = 0; + status_LOWBATT = 0; + status_REPLACEBATT = 0; + status_NOBATT = 0; + status_SLAVE = 0; + status_SLAVEDOWN = 0; + status_COMMLOST = 0; + status_SHUTTING_DOWN = 0; + +} +/^BCHARGE.*/ { battery_charge = \$3 * 100 }; +/^BATTV.*/ { battery_voltage = \$3 * 100 }; +/^NOMBATTV.*/ { battery_voltage_nominal = \$3 * 100 }; +/^LINEV.*/ { input_voltage = \$3 * 100 }; +/^MINLINEV.*/ { input_voltage_min = \$3 * 100 }; +/^MAXLINEV.*/ { input_voltage_max = \$3 * 100 }; +/^LINEFREQ.*/ { input_frequency = \$3 * 100 }; +/^OUTPUTV.*/ { output_voltage = \$3 * 100 }; +/^NOMOUTV.*/ { output_voltage_nominal = \$3 * 100 }; +/^LOADPCT.*/ { load = \$3 * 100 }; +/^ITEMP.*/ { temp = \$3 * 100 }; +/^NOMPOWER.*/ { nompower = \$3 }; +/^TIMELEFT.*/ { time = \$3 * 100 }; +/^STATUS.*/ { online=(\$3 != \"COMMLOST\" && !(\$3 == \"SHUTTING\" && \$4 == \"DOWN\"))?1:0; }; +/^SELFTEST.*/ { selftest_OK = (\$3 == \"OK\") ? 1 : 0; + selftest_NO = (\$3 == \"NO\") ? 1 : 0; + selftest_BT = (\$3 == \"BT\") ? 1 : 0; + selftest_NG = (\$3 == \"NG\") ? 1 : 0; + }; +/^STATUS.*/ { status_ONLINE = (\$3 == \"ONLINE\") ? 1 : 0; + status_CAL = (\$3 == \"CAL\") ? 1 : 0; + status_TRIM = (\$3 == \"TRIM\") ? 1 : 0; + status_BOOST = (\$3 == \"BOOST\") ? 1 : 0; + status_ONBATT = (\$3 == \"ONBATT\") ? 1 : 0; + status_OVERLOAD = (\$3 == \"OVERLOAD\") ? 1 : 0; + status_LOWBATT = (\$3 == \"LOWBATT\") ? 1 : 0; + status_REPLACEBATT = (\$3 == \"REPLACEBATT\") ? 1 : 0; + status_NOBATT = (\$3 == \"NOBATT\") ? 1 : 0; + status_SLAVE = (\$3 == \"SLAVE\") ? 1 : 0; + status_SLAVEDOWN = (\$3 == \"SLAVEDOWN\") ? 1 : 0; + status_COMMLOST = (\$3 == \"COMMLOST\") ? 1 : 0; + status_SHUTTING_DOWN = (\$3 == \"SHUTTING\" && \$4 == \"DOWN\") ? 1 : 0; + }; + +END { + { load_usage = nompower * load / 100 }; + + print \"BEGIN apcupsd_${host}.online $1\"; + print \"SET online = \" online; + print \"END\" + + if (online == 1) { + print \"BEGIN apcupsd_${host}.charge $1\"; + print \"SET battery_charge = \" battery_charge; + print \"END\" + + print \"BEGIN apcupsd_${host}.battery_voltage $1\"; + print \"SET battery_voltage = \" battery_voltage; + print \"SET battery_voltage_nominal = \" battery_voltage_nominal; + print \"END\" + + print \"BEGIN apcupsd_${host}.input_voltage $1\"; + print \"SET input_voltage = \" input_voltage; + print \"SET input_voltage_min = \" input_voltage_min; + print \"SET input_voltage_max = \" input_voltage_max; + print \"END\" + + print \"BEGIN apcupsd_${host}.input_frequency $1\"; + print \"SET input_frequency = \" input_frequency; + print \"END\" + + print \"BEGIN apcupsd_${host}.output_voltage $1\"; + print \"SET output_voltage = \" output_voltage; + print \"SET output_voltage_nominal = \" output_voltage_nominal; + print \"END\" + + print \"BEGIN apcupsd_${host}.load $1\"; + print \"SET load = \" load; + print \"END\" + + print \"BEGIN apcupsd_${host}.load_usage $1\"; + print \"SET load_usage = \" load_usage; + print \"END\" + + print \"BEGIN apcupsd_${host}.temp $1\"; + print \"SET temp = \" temp; + print \"END\" + + print \"BEGIN apcupsd_${host}.time $1\"; + print \"SET time = \" time; + print \"END\" + + print \"BEGIN apcupsd_${host}.selftest $1\"; + print \"SET selftest_OK = \" selftest_OK; + print \"SET selftest_NO = \" selftest_NO; + print \"SET selftest_BT = \" selftest_BT; + print \"SET selftest_NG = \" selftest_NG; + print \"END\" + + print \"BEGIN apcupsd_${host}.status $1\"; + print \"SET status_ONLINE = \" status_ONLINE; + print \"SET status_ONBATT = \" status_ONBATT; + print \"SET status_OVERLOAD = \" status_OVERLOAD; + print \"SET status_LOWBATT = \" status_LOWBATT; + print \"SET status_REPLACEBATT = \" status_REPLACEBATT; + print \"SET status_NOBATT = \" status_NOBATT; + print \"SET status_SLAVE = \" status_SLAVE; + print \"SET status_SLAVEDOWN = \" status_SLAVEDOWN; + print \"SET status_COMMLOST = \" status_COMMLOST; + print \"SET status_CAL = \" status_CAL; + print \"SET status_TRIM = \" status_TRIM; + print \"SET status_BOOST = \" status_BOOST; + print \"SET status_SHUTTING_DOWN = \" status_SHUTTING_DOWN; + print \"END\"; + } +}" + # shellcheck disable=SC2181 + if [ $? -ne 0 ]; then + failed=$((failed + 1)) + error "failed to get values for APC UPS ${host} on ${apcupsd_sources[${host}]}" && return 1 + else + working=$((working + 1)) + fi + done + + [ $working -eq 0 ] && error "failed to get values from all APC UPSes" && return 1 + + return 0 +} diff --git a/src/collectors/charts.d.plugin/apcupsd/apcupsd.conf b/src/collectors/charts.d.plugin/apcupsd/apcupsd.conf new file mode 100644 index 000000000..679c0d61b --- /dev/null +++ b/src/collectors/charts.d.plugin/apcupsd/apcupsd.conf @@ -0,0 +1,25 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2018 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +# add all your APC UPSes in this array - uncomment it too +#declare -A apcupsd_sources=( +# ["local"]="127.0.0.1:3551" +#) + +# how long to wait for apcupsd to respond +#apcupsd_timeout=3 + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#apcupsd_update_every=10 + +# the charts priority on the dashboard +#apcupsd_priority=90000 + +# the number of retries to do in case of failure +# before disabling the module +#apcupsd_retries=10 diff --git a/src/collectors/charts.d.plugin/apcupsd/integrations/apc_ups.md b/src/collectors/charts.d.plugin/apcupsd/integrations/apc_ups.md new file mode 100644 index 000000000..4e3aa3352 --- /dev/null +++ b/src/collectors/charts.d.plugin/apcupsd/integrations/apc_ups.md @@ -0,0 +1,203 @@ +<!--startmeta +custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/collectors/charts.d.plugin/apcupsd/README.md" +meta_yaml: "https://github.com/netdata/netdata/edit/master/src/collectors/charts.d.plugin/apcupsd/metadata.yaml" +sidebar_label: "APC UPS" +learn_status: "Published" +learn_rel_path: "Collecting Metrics/UPS" +most_popular: False +message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE" +endmeta--> + +# APC UPS + + +<img src="https://netdata.cloud/img/apc.svg" width="150"/> + + +Plugin: charts.d.plugin +Module: apcupsd + +<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" /> + +## Overview + +Monitor APC UPS performance with Netdata for optimal uninterruptible power supply operations. Enhance your power supply reliability with real-time APC UPS metrics. + +The collector uses the `apcaccess` tool to contact the `apcupsd` daemon and get the APC UPS statistics. + +This collector is supported on all platforms. + +This collector only supports collecting metrics from a single instance of this integration. + + +### Default Behavior + +#### Auto-Detection + +By default, with no configuration provided, the collector will try to contact 127.0.0.1:3551 with using the `apcaccess` utility. + +#### Limits + +The default configuration for this integration does not impose any limits on data collection. + +#### Performance Impact + +The default configuration for this integration is not expected to impose a significant performance impact on the system. + + +## Metrics + +Metrics grouped by *scope*. + +The scope defines the instance that the metric belongs to. An instance is uniquely identified by a set of labels. + + + +### Per ups + +Metrics related to UPS. Each UPS provides its own set of the following metrics. + +This scope has no labels. + +Metrics: + +| Metric | Dimensions | Unit | +|:------|:----------|:----| +| apcupsd.charge | charge | percentage | +| apcupsd.battery.voltage | voltage, nominal | Volts | +| apcupsd.input.voltage | voltage, min, max | Volts | +| apcupsd.output.voltage | absolute, nominal | Volts | +| apcupsd.input.frequency | frequency | Hz | +| apcupsd.load | load | percentage | +| apcupsd.load_usage | load | Watts | +| apcupsd.temperature | temp | Celsius | +| apcupsd.time | time | Minutes | +| apcupsd.online | online | boolean | +| apcupsd.selftest | OK, NO, BT, NG | status | +| apcupsd.status | ONLINE, ONBATT, OVERLOAD, LOWBATT, REPLACEBATT, NOBATT, SLAVE, SLAVEDOWN, COMMLOST, CAL, TRIM, BOOST, SHUTTING_DOWN | status | + + + +## Alerts + + +The following alerts are available: + +| Alert name | On metric | Description | +|:------------|:----------|:------------| +| [ apcupsd_ups_charge ](https://github.com/netdata/netdata/blob/master/src/health/health.d/apcupsd.conf) | apcupsd.charge | average UPS charge over the last minute | +| [ apcupsd_10min_ups_load ](https://github.com/netdata/netdata/blob/master/src/health/health.d/apcupsd.conf) | apcupsd.load | average UPS load over the last 10 minutes | +| [ apcupsd_last_collected_secs ](https://github.com/netdata/netdata/blob/master/src/health/health.d/apcupsd.conf) | apcupsd.load | number of seconds since the last successful data collection | +| [ apcupsd_selftest_warning ](https://github.com/netdata/netdata/blob/master/src/health/health.d/apcupsd.conf) | apcupsd.selftest | self-test failed due to insufficient battery capacity or due to overload. | +| [ apcupsd_status_onbatt ](https://github.com/netdata/netdata/blob/master/src/health/health.d/apcupsd.conf) | apcupsd.status | APC UPS has switched to battery power because the input power has failed | +| [ apcupsd_status_overload ](https://github.com/netdata/netdata/blob/master/src/health/health.d/apcupsd.conf) | apcupsd.status | APC UPS is overloaded and cannot supply enough power to the load | +| [ apcupsd_status_lowbatt ](https://github.com/netdata/netdata/blob/master/src/health/health.d/apcupsd.conf) | apcupsd.status | APC UPS battery is low and needs to be recharged | +| [ apcupsd_status_replacebatt ](https://github.com/netdata/netdata/blob/master/src/health/health.d/apcupsd.conf) | apcupsd.status | APC UPS battery has reached the end of its lifespan and needs to be replaced | +| [ apcupsd_status_nobatt ](https://github.com/netdata/netdata/blob/master/src/health/health.d/apcupsd.conf) | apcupsd.status | APC UPS has no battery | +| [ apcupsd_status_commlost ](https://github.com/netdata/netdata/blob/master/src/health/health.d/apcupsd.conf) | apcupsd.status | APC UPS communication link is lost | + + +## Setup + +### Prerequisites + +#### Install charts.d plugin + +If [using our official native DEB/RPM packages](https://github.com/netdata/netdata/blob/master/packaging/installer/UPDATE.md#determine-which-installation-method-you-used), make sure `netdata-plugin-chartsd` is installed. + + +#### Required software + +Make sure the `apcaccess` and `apcupsd` are installed and running. + + +### Configuration + +#### File + +The configuration file name for this integration is `charts.d/apcupsd.conf`. + + +You can edit the configuration file using the `edit-config` script from the +Netdata [config directory](https://github.com/netdata/netdata/blob/master/docs/netdata-agent/configuration.md#the-netdata-config-directory). + +```bash +cd /etc/netdata 2>/dev/null || cd /opt/netdata/etc/netdata +sudo ./edit-config charts.d/apcupsd.conf +``` +#### Options + +The config file is sourced by the charts.d plugin. It's a standard bash file. + +The following collapsed table contains all the options that can be configured for the apcupsd collector. + + +<details><summary>Config options</summary> + +| Name | Description | Default | Required | +|:----|:-----------|:-------|:--------:| +| apcupsd_sources | This is an array of apcupsd sources. You can have multiple entries there. Please refer to the example below on how to set it. | 127.0.0.1:3551 | no | +| apcupsd_timeout | How long to wait for apcupsd to respond. | 3 | no | +| apcupsd_update_every | The data collection frequency. If unset, will inherit the netdata update frequency. | 1 | no | +| apcupsd_priority | The charts priority on the dashboard. | 90000 | no | +| apcupsd_retries | The number of retries to do in case of failure before disabling the collector. | 10 | no | + +</details> + +#### Examples + +##### Multiple apcupsd sources + +Specify a multiple apcupsd sources along with a custom update interval + +```yaml +# add all your APC UPSes in this array - uncomment it too +declare -A apcupsd_sources=( + ["local"]="127.0.0.1:3551", + ["remote"]="1.2.3.4:3551" +) + +# how long to wait for apcupsd to respond +#apcupsd_timeout=3 + +# the data collection frequency +# if unset, will inherit the netdata update frequency +apcupsd_update_every=5 + +# the charts priority on the dashboard +#apcupsd_priority=90000 + +# the number of retries to do in case of failure +# before disabling the module +#apcupsd_retries=10 + +``` + + +## Troubleshooting + +### Debug Mode + +To troubleshoot issues with the `apcupsd` collector, run the `charts.d.plugin` with the debug option enabled. The output +should give you clues as to why the collector isn't working. + +- Navigate to the `plugins.d` directory, usually at `/usr/libexec/netdata/plugins.d/`. If that's not the case on + your system, open `netdata.conf` and look for the `plugins` setting under `[directories]`. + + ```bash + cd /usr/libexec/netdata/plugins.d/ + ``` + +- Switch to the `netdata` user. + + ```bash + sudo -u netdata -s + ``` + +- Run the `charts.d.plugin` to debug the collector: + + ```bash + ./charts.d.plugin debug 1 apcupsd + ``` + + diff --git a/src/collectors/charts.d.plugin/apcupsd/metadata.yaml b/src/collectors/charts.d.plugin/apcupsd/metadata.yaml new file mode 100644 index 000000000..0af9c98e6 --- /dev/null +++ b/src/collectors/charts.d.plugin/apcupsd/metadata.yaml @@ -0,0 +1,256 @@ +plugin_name: charts.d.plugin +modules: + - meta: + plugin_name: charts.d.plugin + module_name: apcupsd + monitored_instance: + name: APC UPS + link: "https://www.apc.com" + categories: + - data-collection.ups + icon_filename: "apc.svg" + related_resources: + integrations: + list: [] + info_provided_to_referring_integrations: + description: "" + keywords: + - ups + - apc + - power + - supply + - battery + - apcupsd + most_popular: false + overview: + data_collection: + metrics_description: "Monitor APC UPS performance with Netdata for optimal uninterruptible power supply operations. Enhance your power supply reliability with real-time APC UPS metrics." + method_description: "The collector uses the `apcaccess` tool to contact the `apcupsd` daemon and get the APC UPS statistics." + supported_platforms: + include: [] + exclude: [] + multi_instance: false + additional_permissions: + description: "" + default_behavior: + auto_detection: + description: "By default, with no configuration provided, the collector will try to contact 127.0.0.1:3551 with using the `apcaccess` utility." + limits: + description: "" + performance_impact: + description: "" + setup: + prerequisites: + list: + - title: "Install charts.d plugin" + description: | + If [using our official native DEB/RPM packages](https://github.com/netdata/netdata/blob/master/packaging/installer/UPDATE.md#determine-which-installation-method-you-used), make sure `netdata-plugin-chartsd` is installed. + - title: "Required software" + description: "Make sure the `apcaccess` and `apcupsd` are installed and running." + configuration: + file: + name: charts.d/apcupsd.conf + options: + description: | + The config file is sourced by the charts.d plugin. It's a standard bash file. + + The following collapsed table contains all the options that can be configured for the apcupsd collector. + folding: + title: "Config options" + enabled: true + list: + - name: apcupsd_sources + description: This is an array of apcupsd sources. You can have multiple entries there. Please refer to the example below on how to set it. + default_value: "127.0.0.1:3551" + required: false + - name: apcupsd_timeout + description: How long to wait for apcupsd to respond. + default_value: 3 + required: false + - name: apcupsd_update_every + description: The data collection frequency. If unset, will inherit the netdata update frequency. + default_value: 1 + required: false + - name: apcupsd_priority + description: The charts priority on the dashboard. + default_value: 90000 + required: false + - name: apcupsd_retries + description: The number of retries to do in case of failure before disabling the collector. + default_value: 10 + required: false + examples: + folding: + enabled: false + title: "Config" + list: + - name: Multiple apcupsd sources + description: Specify a multiple apcupsd sources along with a custom update interval + config: | + # add all your APC UPSes in this array - uncomment it too + declare -A apcupsd_sources=( + ["local"]="127.0.0.1:3551", + ["remote"]="1.2.3.4:3551" + ) + + # how long to wait for apcupsd to respond + #apcupsd_timeout=3 + + # the data collection frequency + # if unset, will inherit the netdata update frequency + apcupsd_update_every=5 + + # the charts priority on the dashboard + #apcupsd_priority=90000 + + # the number of retries to do in case of failure + # before disabling the module + #apcupsd_retries=10 + troubleshooting: + problems: + list: [] + alerts: + - name: apcupsd_ups_charge + link: https://github.com/netdata/netdata/blob/master/src/health/health.d/apcupsd.conf + metric: apcupsd.charge + info: average UPS charge over the last minute + os: "*" + - name: apcupsd_10min_ups_load + link: https://github.com/netdata/netdata/blob/master/src/health/health.d/apcupsd.conf + metric: apcupsd.load + info: average UPS load over the last 10 minutes + os: "*" + - name: apcupsd_last_collected_secs + link: https://github.com/netdata/netdata/blob/master/src/health/health.d/apcupsd.conf + metric: apcupsd.load + info: number of seconds since the last successful data collection + - name: apcupsd_selftest_warning + link: https://github.com/netdata/netdata/blob/master/src/health/health.d/apcupsd.conf + metric: apcupsd.selftest + info: self-test failed due to insufficient battery capacity or due to overload. + - name: apcupsd_status_onbatt + link: https://github.com/netdata/netdata/blob/master/src/health/health.d/apcupsd.conf + metric: apcupsd.status + info: APC UPS has switched to battery power because the input power has failed + - name: apcupsd_status_overload + link: https://github.com/netdata/netdata/blob/master/src/health/health.d/apcupsd.conf + metric: apcupsd.status + info: APC UPS is overloaded and cannot supply enough power to the load + - name: apcupsd_status_lowbatt + link: https://github.com/netdata/netdata/blob/master/src/health/health.d/apcupsd.conf + metric: apcupsd.status + info: APC UPS battery is low and needs to be recharged + - name: apcupsd_status_replacebatt + link: https://github.com/netdata/netdata/blob/master/src/health/health.d/apcupsd.conf + metric: apcupsd.status + info: APC UPS battery has reached the end of its lifespan and needs to be replaced + - name: apcupsd_status_nobatt + link: https://github.com/netdata/netdata/blob/master/src/health/health.d/apcupsd.conf + metric: apcupsd.status + info: APC UPS has no battery + - name: apcupsd_status_commlost + link: https://github.com/netdata/netdata/blob/master/src/health/health.d/apcupsd.conf + metric: apcupsd.status + info: APC UPS communication link is lost + metrics: + folding: + title: Metrics + enabled: false + description: "" + availability: [] + scopes: + - name: ups + description: "Metrics related to UPS. Each UPS provides its own set of the following metrics." + labels: [] + metrics: + - name: apcupsd.charge + description: UPS Charge + unit: "percentage" + chart_type: area + dimensions: + - name: charge + - name: apcupsd.battery.voltage + description: UPS Battery Voltage + unit: "Volts" + chart_type: line + dimensions: + - name: voltage + - name: nominal + - name: apcupsd.input.voltage + description: UPS Input Voltage + unit: "Volts" + chart_type: line + dimensions: + - name: voltage + - name: min + - name: max + - name: apcupsd.output.voltage + description: UPS Output Voltage + unit: "Volts" + chart_type: line + dimensions: + - name: absolute + - name: nominal + - name: apcupsd.input.frequency + description: UPS Input Voltage + unit: "Hz" + chart_type: line + dimensions: + - name: frequency + - name: apcupsd.load + description: UPS Load + unit: "percentage" + chart_type: area + dimensions: + - name: load + - name: apcupsd.load_usage + description: UPS Load Usage + unit: "Watts" + chart_type: area + dimensions: + - name: load + - name: apcupsd.temperature + description: UPS Temperature + unit: "Celsius" + chart_type: line + dimensions: + - name: temp + - name: apcupsd.time + description: UPS Time Remaining + unit: "Minutes" + chart_type: area + dimensions: + - name: time + - name: apcupsd.online + description: UPS ONLINE flag + unit: "boolean" + chart_type: line + dimensions: + - name: online + - name: apcupsd.selftest + description: UPS Self-Test status + unit: status + chart_type: line + dimensions: + - name: OK + - name: NO + - name: BT + - name: NG + - name: apcupsd.status + description: UPS Status + unit: status + chart_type: line + dimensions: + - name: ONLINE + - name: ONBATT + - name: OVERLOAD + - name: LOWBATT + - name: REPLACEBATT + - name: NOBATT + - name: SLAVE + - name: SLAVEDOWN + - name: COMMLOST + - name: CAL + - name: TRIM + - name: BOOST + - name: SHUTTING_DOWN diff --git a/src/collectors/charts.d.plugin/charts.d.conf b/src/collectors/charts.d.plugin/charts.d.conf new file mode 100644 index 000000000..4614f259e --- /dev/null +++ b/src/collectors/charts.d.plugin/charts.d.conf @@ -0,0 +1,47 @@ +# This is the configuration for charts.d.plugin + +# Each of its collectors can read configuration either from this file +# or a NAME.conf file (where NAME is the collector name). +# The collector specific file has higher precedence. + +# This file is a shell script too. + +# ----------------------------------------------------------------------------- + +# number of seconds to run without restart +# after this time, charts.d.plugin will exit +# netdata will restart it, but a small gap +# will appear in the charts.d.plugin charts. +#restart_timeout=$[3600 * 4] + +# when making iterations, charts.d can loop more frequently +# to prevent plugins missing iterations. +# this is a percentage relative to update_every to align its +# iterations. +# The minimum is 10%, the maximum 100%. +# So, if update_every is 1 second and time_divisor is 50, +# charts.d will iterate every 500ms. +# Charts will be called to collect data only if the time +# passed since the last time the collected data is equal or +# above their update_every. +#time_divisor=50 + +# ----------------------------------------------------------------------------- + +# the default enable/disable for all charts.d collectors +# the default is "yes" +# enable_all_charts="yes" + +# BY DEFAULT ENABLED MODULES +# ap=yes +# apcupsd=yes +# libreswan=yes +# opensips=yes + +# ----------------------------------------------------------------------------- +# THESE NEED TO BE SET TO "force" TO BE ENABLED + +# Nothing useful. +# Just an example charts.d plugin you can use as a template. +# example=force +# sensors=force diff --git a/src/collectors/charts.d.plugin/charts.d.dryrun-helper.sh b/src/collectors/charts.d.plugin/charts.d.dryrun-helper.sh new file mode 100755 index 000000000..91af2c542 --- /dev/null +++ b/src/collectors/charts.d.plugin/charts.d.dryrun-helper.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-or-later + +# shellcheck disable=SC2181 + +# will stop the script for any error +set -e + +me="$0" +name="$1" +chart="$2" +conf="$3" + +can_diff=1 + +tmp1="$(mktemp)" +tmp2="$(mktemp)" + +myset() { + set | grep -v "^_=" | grep -v "^PIPESTATUS=" | grep -v "^BASH_LINENO=" +} + +# save 2 'set' +myset >"$tmp1" +myset >"$tmp2" + +# make sure they don't differ +diff "$tmp1" "$tmp2" >/dev/null 2>&1 +if [ $? -ne 0 ]; then + # they differ, we cannot do the check + echo >&2 "$me: cannot check with diff." + can_diff=0 +fi + +# do it again, now including the script +myset >"$tmp1" + +# include the plugin and its config +if [ -f "$conf" ]; then + # shellcheck source=/dev/null + . "$conf" + if [ $? -ne 0 ]; then + echo >&2 "$me: cannot load config file $conf" + rm "$tmp1" "$tmp2" + exit 1 + fi +fi + +# shellcheck source=/dev/null +. "$chart" +if [ $? -ne 0 ]; then + echo >&2 "$me: cannot load chart file $chart" + rm "$tmp1" "$tmp2" + exit 1 +fi + +# remove all variables starting with the plugin name +myset | grep -v "^$name" >"$tmp2" + +if [ $can_diff -eq 1 ]; then + # check if they are different + # make sure they don't differ + diff "$tmp1" "$tmp2" >&2 + if [ $? -ne 0 ]; then + # they differ + rm "$tmp1" "$tmp2" + exit 1 + fi +fi + +rm "$tmp1" "$tmp2" +exit 0 diff --git a/src/collectors/charts.d.plugin/charts.d.plugin.in b/src/collectors/charts.d.plugin/charts.d.plugin.in new file mode 100755 index 000000000..4e64b7e23 --- /dev/null +++ b/src/collectors/charts.d.plugin/charts.d.plugin.in @@ -0,0 +1,809 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-or-later + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2017 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# +# charts.d.plugin allows easy development of BASH plugins +# +# if you need to run parallel charts.d processes, link this file to a different name +# in the same directory, with a .plugin suffix and netdata will start both of them, +# each will have a different config file and modules configuration directory. +# + +export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin:@sbindir_POST@" + +PROGRAM_FILE="$0" +MODULE_NAME="main" + +# ----------------------------------------------------------------------------- +# logging + +PROGRAM_NAME="$(basename "${0}")" +SHORT_PROGRAM_NAME="${PROGRAM_NAME/.plugin/}" + +# these should be the same with syslog() priorities +NDLP_EMERG=0 # system is unusable +NDLP_ALERT=1 # action must be taken immediately +NDLP_CRIT=2 # critical conditions +NDLP_ERR=3 # error conditions +NDLP_WARN=4 # warning conditions +NDLP_NOTICE=5 # normal but significant condition +NDLP_INFO=6 # informational +NDLP_DEBUG=7 # debug-level messages + +# the max (numerically) log level we will log +LOG_LEVEL=$NDLP_INFO + +set_log_min_priority() { + case "${NETDATA_LOG_LEVEL,,}" in + "emerg" | "emergency") + LOG_LEVEL=$NDLP_EMERG + ;; + + "alert") + LOG_LEVEL=$NDLP_ALERT + ;; + + "crit" | "critical") + LOG_LEVEL=$NDLP_CRIT + ;; + + "err" | "error") + LOG_LEVEL=$NDLP_ERR + ;; + + "warn" | "warning") + LOG_LEVEL=$NDLP_WARN + ;; + + "notice") + LOG_LEVEL=$NDLP_NOTICE + ;; + + "info") + LOG_LEVEL=$NDLP_INFO + ;; + + "debug") + LOG_LEVEL=$NDLP_DEBUG + ;; + esac +} + +set_log_min_priority + +log() { + local level="${1}" + shift 1 + + [[ -n "$level" && -n "$LOG_LEVEL" && "$level" -gt "$LOG_LEVEL" ]] && return + + systemd-cat-native --log-as-netdata --newline="--NEWLINE--" <<EOFLOG +INVOCATION_ID=${NETDATA_INVOCATION_ID} +SYSLOG_IDENTIFIER=${PROGRAM_NAME} +PRIORITY=${level} +THREAD_TAG=charts.d.plugin +ND_LOG_SOURCE=collector +MESSAGE=${MODULE_NAME}: ${*//\\n/--NEWLINE--} + +EOFLOG + # AN EMPTY LINE IS NEEDED ABOVE +} + +info() { + log "$NDLP_INFO" "${@}" +} + +warning() { + log "$NDLP_WARN" "${@}" +} + +error() { + log "$NDLP_ERR" "${@}" +} + +fatal() { + log "$NDLP_ALERT" "${@}" + echo "DISABLE" + exit 1 +} + +debug() { + [ "$debug" = "1" ] && log "$NDLP_DEBUG" "${@}" +} + +# ----------------------------------------------------------------------------- +# check for BASH v4+ (required for associative arrays) + +if [ ${BASH_VERSINFO[0]} -lt 4 ]; then + echo >&2 "BASH version 4 or later is required (this is ${BASH_VERSION})." + exit 1 +fi + +# ----------------------------------------------------------------------------- +# create temp dir + +debug=0 +TMP_DIR= +chartsd_cleanup() { + trap '' EXIT QUIT HUP INT TERM + + if [ ! -z "$TMP_DIR" -a -d "$TMP_DIR" ]; then + [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: cleaning up temporary directory $TMP_DIR ..." + rm -rf "$TMP_DIR" + fi + echo "EXIT" + exit 0 +} +trap chartsd_cleanup EXIT QUIT HUP INT TERM + +if [ $UID = "0" ]; then + TMP_DIR="$(mktemp -d /var/run/netdata-${PROGRAM_NAME}-XXXXXXXXXX)" +else + TMP_DIR="$(mktemp -d /tmp/.netdata-${PROGRAM_NAME}-XXXXXXXXXX)" +fi + +logdate() { + date "+%Y-%m-%d %H:%M:%S" +} + +# ----------------------------------------------------------------------------- +# check a few commands + +require_cmd() { + local x=$(which "${1}" 2>/dev/null || command -v "${1}" 2>/dev/null) + if [ -z "${x}" -o ! -x "${x}" ]; then + warning "command '${1}' is not found in ${PATH}." + eval "${1^^}_CMD=\"\"" + return 1 + fi + + eval "${1^^}_CMD=\"${x}\"" + return 0 +} + +require_cmd date || exit 1 +require_cmd sed || exit 1 +require_cmd basename || exit 1 +require_cmd dirname || exit 1 +require_cmd cat || exit 1 +require_cmd grep || exit 1 +require_cmd egrep || exit 1 +require_cmd mktemp || exit 1 +require_cmd awk || exit 1 +require_cmd timeout || exit 1 +require_cmd curl || exit 1 + +# ----------------------------------------------------------------------------- + +[ $((BASH_VERSINFO[0])) -lt 4 ] && fatal "BASH version 4 or later is required, but found version: ${BASH_VERSION}. Please upgrade." + +info "started from '$PROGRAM_FILE' with options: $*" + +# ----------------------------------------------------------------------------- +# internal defaults +# netdata exposes a few environment variables for us + +[ -z "${NETDATA_PLUGINS_DIR}" ] && NETDATA_PLUGINS_DIR="$(dirname "${0}")" +[ -z "${NETDATA_USER_CONFIG_DIR}" ] && NETDATA_USER_CONFIG_DIR="@configdir_POST@" +[ -z "${NETDATA_STOCK_CONFIG_DIR}" ] && NETDATA_STOCK_CONFIG_DIR="@libconfigdir_POST@" + +pluginsd="${NETDATA_PLUGINS_DIR}" +stockconfd="${NETDATA_STOCK_CONFIG_DIR}/${SHORT_PROGRAM_NAME}" +userconfd="${NETDATA_USER_CONFIG_DIR}/${SHORT_PROGRAM_NAME}" +olduserconfd="${NETDATA_USER_CONFIG_DIR}" +chartsd="$pluginsd/../charts.d" + +minimum_update_frequency="${NETDATA_UPDATE_EVERY-1}" +update_every=${minimum_update_frequency} # this will be overwritten by the command line + +# work around for non BASH shells +charts_create="_create" +charts_update="_update" +charts_check="_check" +charts_underscore="_" + +# when making iterations, charts.d can loop more frequently +# to prevent plugins missing iterations. +# this is a percentage relative to update_every to align its +# iterations. +# The minimum is 10%, the maximum 100%. +# So, if update_every is 1 second and time_divisor is 50, +# charts.d will iterate every 500ms. +# Charts will be called to collect data only if the time +# passed since the last time the collected data is equal or +# above their update_every. +time_divisor=50 + +# number of seconds to run without restart +# after this time, charts.d.plugin will exit +# netdata will restart it +restart_timeout=$((3600 * 4)) + +# check if the charts.d plugins are using global variables +# they should not. +# It does not currently support BASH v4 arrays, so it is +# disabled +dryrunner=0 + +# check for timeout command +check_for_timeout=1 + +# the default enable/disable value for all charts +enable_all_charts="yes" + +# ----------------------------------------------------------------------------- +# parse parameters + +check=0 +chart_only= +while [ ! -z "$1" ]; do + if [ "$1" = "check" ]; then + check=1 + shift + continue + fi + + if [ "$1" = "debug" -o "$1" = "all" ]; then + debug=1 + LOG_LEVEL=$NDLP_DEBUG + shift + continue + fi + + if [ -f "$chartsd/$1.chart.sh" ]; then + debug=1 + LOG_LEVEL=$NDLP_DEBUG + chart_only="$(echo $1.chart.sh | sed "s/\.chart\.sh$//g")" + shift + continue + fi + + if [ -f "$chartsd/$1" ]; then + debug=1 + LOG_LEVEL=$NDLP_DEBUG + chart_only="$(echo $1 | sed "s/\.chart\.sh$//g")" + shift + continue + fi + + # number check + n="$1" + x=$((n)) + if [ "$x" = "$n" ]; then + shift + update_every=$x + [ $update_every -lt $minimum_update_frequency ] && update_every=$minimum_update_frequency + continue + fi + + fatal "Cannot understand parameter $1. Aborting." +done + +# ----------------------------------------------------------------------------- +# loop control + +# default sleep function +LOOPSLEEPMS_HIGHRES=0 +now_ms= +current_time_ms_default() { + now_ms="$(date +'%s')000" +} +current_time_ms="current_time_ms_default" +current_time_ms_accuracy=1 +mysleep="sleep" + +# if found and included, this file overwrites loopsleepms() +# and current_time_ms() with a high resolution timer function +# for precise looping. +source "$pluginsd/loopsleepms.sh.inc" +[ $? -ne 0 ] && error "Failed to load '$pluginsd/loopsleepms.sh.inc'." + +# ----------------------------------------------------------------------------- +# load my configuration + +for myconfig in "${NETDATA_STOCK_CONFIG_DIR}/${SHORT_PROGRAM_NAME}.conf" "${NETDATA_USER_CONFIG_DIR}/${SHORT_PROGRAM_NAME}.conf"; do + if [ -f "$myconfig" ]; then + source "$myconfig" + if [ $? -ne 0 ]; then + error "Config file '$myconfig' loaded with errors." + else + info "Configuration file '$myconfig' loaded." + fi + else + warning "Configuration file '$myconfig' not found." + fi +done + +# make sure time_divisor is right +time_divisor=$((time_divisor)) +[ $time_divisor -lt 10 ] && time_divisor=10 +[ $time_divisor -gt 100 ] && time_divisor=100 + +# we check for the timeout command, after we load our +# configuration, so that the user may overwrite the +# timeout command we use, providing a function that +# can emulate the timeout command we need: +# > timeout SECONDS command ... +if [ $check_for_timeout -eq 1 ]; then + require_cmd timeout || exit 1 +fi + +# ----------------------------------------------------------------------------- +# internal checks + +# netdata passes the requested update frequency as the first argument +update_every=$((update_every + 1 - 1)) # makes sure it is a number +test $update_every -eq 0 && update_every=1 # if it is zero, make it 1 + +# check the charts.d directory +[ ! -d "$chartsd" ] && fatal "cannot find charts directory '$chartsd'" + +# ----------------------------------------------------------------------------- +# library functions + +fixid() { + echo "$*" | + tr -c "[A-Z][a-z][0-9]" "_" | + sed -e "s|^_\+||g" -e "s|_\+$||g" -e "s|_\+|_|g" | + tr "[A-Z]" "[a-z]" +} + +isvarset() { + [ -n "$1" ] && [ "$1" != "unknown" ] && [ "$1" != "none" ] + return $? +} + +getosid() { + if isvarset "${NETDATA_CONTAINER_OS_ID}"; then + echo "${NETDATA_CONTAINER_OS_ID}" + else + echo "${NETDATA_SYSTEM_OS_ID}" + fi +} + +run() { + local ret pid="${BASHPID}" t + + if [ "z${1}" = "z-t" -a "${2}" != "0" ]; then + t="${2}" + shift 2 + timeout "${t}" "${@}" 2>"${TMP_DIR}/run.${pid}" + ret=$? + else + "${@}" 2>"${TMP_DIR}/run.${pid}" + ret=$? + fi + + if [ ${ret} -ne 0 ]; then + { + printf "$(logdate): ${PROGRAM_NAME}: ${status}: ${MODULE_NAME}: command '" + printf "%q " "${@}" + printf "' failed with code ${ret}:\n --- BEGIN TRACE ---\n" + cat "${TMP_DIR}/run.${pid}" + printf " --- END TRACE ---\n" + } >&2 + fi + rm -f "${TMP_DIR}/run.${pid}" + + return ${ret} +} + +# convert any floating point number +# to integer, give a multiplier +# the result is stored in ${FLOAT2INT_RESULT} +# so that no fork is necessary +# the multiplier must be a power of 10 +float2int() { + local f m="$2" a b l v=($1) + f=${v[0]} + + # the length of the multiplier - 1 + l=$((${#m} - 1)) + + # check if the number is in scientific notation + if [[ ${f} =~ ^[[:space:]]*(-)?[0-9.]+(e|E)(\+|-)[0-9]+ ]]; then + # convert it to decimal + # unfortunately, this fork cannot be avoided + # if you know of a way to avoid it, please let me know + f=$(printf "%0.${l}f" ${f}) + fi + + # split the floating point number + # in integer (a) and decimal (b) + a=${f/.*/} + b=${f/*./} + + # if the integer part is missing + # set it to zero + [ -z "${a}" ] && a="0" + + # strip leading zeros from the integer part + # base 10 conversion + a=$((10#$a)) + + # check the length of the decimal part + # against the length of the multiplier + if [ ${#b} -gt ${l} ]; then + # too many digits - take the most significant + b=${b:0:l} + + elif [ ${#b} -lt ${l} ]; then + # too few digits - pad with zero on the right + local z="00000000000000000000000" r=$((l - ${#b})) + b="${b}${z:0:r}" + fi + + # strip leading zeros from the decimal part + # base 10 conversion + b=$((10#$b)) + + # store the result + FLOAT2INT_RESULT=$(((a * m) + b)) +} + +# ----------------------------------------------------------------------------- +# charts check functions + +all_charts() { + cd "$chartsd" + [ $? -ne 0 ] && error "cannot cd to $chartsd" && return 1 + + ls *.chart.sh | sed "s/\.chart\.sh$//g" +} + +declare -A charts_enable_keyword=( + ['apache']="force" + ['cpu_apps']="force" + ['cpufreq']="force" + ['example']="force" + ['exim']="force" + ['hddtemp']="force" + ['load_average']="force" + ['mem_apps']="force" + ['mysql']="force" + ['nginx']="force" + ['phpfpm']="force" + ['postfix']="force" + ['sensors']="force" + ['squid']="force" + ['tomcat']="force" +) + +declare -A obsolete_charts=( + ['apache']="python.d.plugin module" + ['cpu_apps']="apps.plugin" + ['cpufreq']="proc plugin" + ['exim']="python.d.plugin module" + ['hddtemp']="python.d.plugin module" + ['load_average']="proc plugin" + ['mem_apps']="proc plugin" + ['mysql']="python.d.plugin module" + ['nginx']="python.d.plugin module" + ['phpfpm']="python.d.plugin module" + ['postfix']="python.d.plugin module" + ['squid']="python.d.plugin module" + ['tomcat']="python.d.plugin module" +) + +all_enabled_charts() { + local charts enabled required + + # find all enabled charts + for chart in $(all_charts); do + MODULE_NAME="${chart}" + + if [ -n "${obsolete_charts["$MODULE_NAME"]}" ]; then + debug "is replaced by ${obsolete_charts["$MODULE_NAME"]}, skipping it." + continue + fi + + eval "enabled=\$$chart" + if [ -z "${enabled}" ]; then + enabled="${enable_all_charts}" + fi + + required="${charts_enable_keyword[${chart}]}" + [ -z "${required}" ] && required="yes" + + if [ ! "${enabled}" = "${required}" ]; then + info "is disabled. Add a line with $chart=$required in '${NETDATA_USER_CONFIG_DIR}/${PROGRAM_NAME}.conf' to enable it (or remove the line that disables it)." + else + debug "is enabled for auto-detection." + local charts="$charts $chart" + fi + done + MODULE_NAME="main" + + local charts2= + for chart in $charts; do + MODULE_NAME="${chart}" + + # check the enabled charts + local check="$(cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_check()")" + if [ -z "$check" ]; then + error "module '$chart' does not seem to have a $chart$charts_check() function. Disabling it." + continue + fi + + local create="$(cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_create()")" + if [ -z "$create" ]; then + error "module '$chart' does not seem to have a $chart$charts_create() function. Disabling it." + continue + fi + + local update="$(cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_update()")" + if [ -z "$update" ]; then + error "module '$chart' does not seem to have a $chart$charts_update() function. Disabling it." + continue + fi + + # check its config + #if [ -f "$userconfd/$chart.conf" ] + #then + # if [ ! -z "$( cat "$userconfd/$chart.conf" | sed "s/^ \+//g" | grep -v "^$" | grep -v "^#" | grep -v "^$chart$charts_underscore" )" ] + # then + # error "module's $chart config $userconfd/$chart.conf should only have lines starting with $chart$charts_underscore . Disabling it." + # continue + # fi + #fi + + #if [ $dryrunner -eq 1 ] + # then + # "$pluginsd/charts.d.dryrun-helper.sh" "$chart" "$chartsd/$chart.chart.sh" "$userconfd/$chart.conf" >/dev/null + # if [ $? -ne 0 ] + # then + # error "module's $chart did not pass the dry run check. This means it uses global variables not starting with $chart. Disabling it." + # continue + # fi + #fi + + local charts2="$charts2 $chart" + done + MODULE_NAME="main" + + echo $charts2 + debug "enabled charts: $charts2" +} + +# ----------------------------------------------------------------------------- +# load the charts + +suffix_retries="_retries" +suffix_update_every="_update_every" +active_charts= +for chart in $(all_enabled_charts); do + MODULE_NAME="${chart}" + + debug "loading module: '$chartsd/$chart.chart.sh'" + + source "$chartsd/$chart.chart.sh" + [ $? -ne 0 ] && warning "Module '$chartsd/$chart.chart.sh' loaded with errors." + + # first load the stock config + if [ -f "$stockconfd/$chart.conf" ]; then + debug "loading module configuration: '$stockconfd/$chart.conf'" + source "$stockconfd/$chart.conf" + [ $? -ne 0 ] && warning "Config file '$stockconfd/$chart.conf' loaded with errors." + else + debug "not found module configuration: '$stockconfd/$chart.conf'" + fi + + # then load the user config (it overwrites the stock) + if [ -f "$userconfd/$chart.conf" ]; then + debug "loading module configuration: '$userconfd/$chart.conf'" + source "$userconfd/$chart.conf" + [ $? -ne 0 ] && warning "Config file '$userconfd/$chart.conf' loaded with errors." + else + debug "not found module configuration: '$userconfd/$chart.conf'" + + if [ -f "$olduserconfd/$chart.conf" ]; then + # support for very old netdata that had the charts.d module configs in /etc/netdata + info "loading module configuration from obsolete location: '$olduserconfd/$chart.conf'" + source "$olduserconfd/$chart.conf" + [ $? -ne 0 ] && warning "Config file '$olduserconfd/$chart.conf' loaded with errors." + fi + fi + + eval "dt=\$$chart$suffix_update_every" + dt=$((dt + 1 - 1)) # make sure it is a number + if [ $dt -lt $update_every ]; then + eval "$chart$suffix_update_every=$update_every" + fi + + $chart$charts_check + if [ $? -eq 0 ]; then + debug "module '$chart' activated" + active_charts="$active_charts $chart" + else + error "module's '$chart' check() function reports failure." + fi +done +MODULE_NAME="main" +debug "activated modules: $active_charts" + +# ----------------------------------------------------------------------------- +# check overwrites + +# enable work time reporting +debug_time= +test $debug -eq 1 && debug_time=tellwork + +# if we only need a specific chart, remove all the others +if [ ! -z "${chart_only}" ]; then + debug "requested to run only for: '${chart_only}'" + check_charts= + for chart in $active_charts; do + if [ "$chart" = "$chart_only" ]; then + check_charts="$chart" + break + fi + done + active_charts="$check_charts" +fi +debug "activated charts: $active_charts" + +# stop if we just need a pre-check +if [ $check -eq 1 ]; then + info "CHECK RESULT" + info "Will run the charts: $active_charts" + exit 0 +fi + +# ----------------------------------------------------------------------------- + +cd "${TMP_DIR}" || exit 1 + +# ----------------------------------------------------------------------------- +# create charts + +run_charts= +for chart in $active_charts; do + MODULE_NAME="${chart}" + + debug "calling '$chart$charts_create()'..." + $chart$charts_create + if [ $? -eq 0 ]; then + run_charts="$run_charts $chart" + debug "'$chart' initialized." + else + error "module's '$chart' function '$chart$charts_create()' reports failure." + fi +done +MODULE_NAME="main" +debug "run_charts='$run_charts'" + +# ----------------------------------------------------------------------------- +# update dimensions + +[ -z "$run_charts" ] && fatal "No charts to collect data from." + +keepalive() { + if [ ! -t 1 ] && ! printf "\n"; then + chartsd_cleanup + fi +} + +declare -A charts_last_update=() charts_update_every=() charts_retries=() charts_next_update=() charts_run_counter=() charts_serial_failures=() +global_update() { + local exit_at \ + c=0 dt ret last_ms exec_start_ms exec_end_ms \ + chart now_charts=() next_charts=($run_charts) \ + next_ms x seconds millis + + # return the current time in ms in $now_ms + ${current_time_ms} + + exit_at=$((now_ms + (restart_timeout * 1000))) + + for chart in $run_charts; do + eval "charts_update_every[$chart]=\$$chart$suffix_update_every" + test -z "${charts_update_every[$chart]}" && charts_update_every[$chart]=$update_every + + eval "charts_retries[$chart]=\$$chart$suffix_retries" + test -z "${charts_retries[$chart]}" && charts_retries[$chart]=10 + + charts_last_update[$chart]=$((now_ms - (now_ms % (charts_update_every[$chart] * 1000)))) + charts_next_update[$chart]=$((charts_last_update[$chart] + (charts_update_every[$chart] * 1000))) + charts_run_counter[$chart]=0 + charts_serial_failures[$chart]=0 + + echo "CHART netdata.plugin_chartsd_$chart '' 'Execution time for $chart plugin' 'milliseconds / run' charts.d netdata.plugin_charts area 145000 ${charts_update_every[$chart]} '' '' '$chart'" + echo "DIMENSION run_time 'run time' absolute 1 1" + done + + # the main loop + while [ "${#next_charts[@]}" -gt 0 ]; do + keepalive + + c=$((c + 1)) + now_charts=("${next_charts[@]}") + next_charts=() + + # return the current time in ms in $now_ms + ${current_time_ms} + + for chart in "${now_charts[@]}"; do + MODULE_NAME="${chart}" + + if [ ${now_ms} -ge ${charts_next_update[$chart]} ]; then + last_ms=${charts_last_update[$chart]} + dt=$((now_ms - last_ms)) + + charts_last_update[$chart]=${now_ms} + + while [ ${charts_next_update[$chart]} -lt ${now_ms} ]; do + charts_next_update[$chart]=$((charts_next_update[$chart] + (charts_update_every[$chart] * 1000))) + done + + # the first call should not give a duration + # so that netdata calibrates to current time + dt=$((dt * 1000)) + charts_run_counter[$chart]=$((charts_run_counter[$chart] + 1)) + if [ ${charts_run_counter[$chart]} -eq 1 ]; then + dt= + fi + + exec_start_ms=$now_ms + $chart$charts_update $dt + ret=$? + + # return the current time in ms in $now_ms + ${current_time_ms} + exec_end_ms=$now_ms + + echo "BEGIN netdata.plugin_chartsd_$chart $dt" + echo "SET run_time = $((exec_end_ms - exec_start_ms))" + echo "END" + + if [ $ret -eq 0 ]; then + charts_serial_failures[$chart]=0 + next_charts+=($chart) + else + charts_serial_failures[$chart]=$((charts_serial_failures[$chart] + 1)) + + if [ ${charts_serial_failures[$chart]} -gt ${charts_retries[$chart]} ]; then + error "module's '$chart' update() function reported failure ${charts_serial_failures[$chart]} times. Disabling it." + else + error "module's '$chart' update() function reports failure. Will keep trying for a while." + next_charts+=($chart) + fi + fi + else + next_charts+=($chart) + fi + done + MODULE_NAME="${chart}" + + # wait the time you are required to + next_ms=$((now_ms + (update_every * 1000 * 100))) + for x in "${charts_next_update[@]}"; do [ ${x} -lt ${next_ms} ] && next_ms=${x}; done + next_ms=$((next_ms - now_ms)) + + if [ ${LOOPSLEEPMS_HIGHRES} -eq 1 -a ${next_ms} -gt 0 ]; then + next_ms=$((next_ms + current_time_ms_accuracy)) + seconds=$((next_ms / 1000)) + millis=$((next_ms % 1000)) + if [ ${millis} -lt 10 ]; then + millis="00${millis}" + elif [ ${millis} -lt 100 ]; then + millis="0${millis}" + fi + + debug "sleeping for ${seconds}.${millis} seconds." + ${mysleep} ${seconds}.${millis} + else + debug "sleeping for ${update_every} seconds." + ${mysleep} $update_every + fi + + test ${now_ms} -ge ${exit_at} && exit 0 + done + + fatal "nothing left to do, exiting..." +} + +global_update diff --git a/src/collectors/charts.d.plugin/example/README.md b/src/collectors/charts.d.plugin/example/README.md new file mode 100644 index 000000000..a16180581 --- /dev/null +++ b/src/collectors/charts.d.plugin/example/README.md @@ -0,0 +1,14 @@ +<!-- +title: "Example" +custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/collectors/charts.d.plugin/example/README.md" +sidebar_label: "example-charts.d.plugin" +learn_status: "Published" +learn_topic_type: "References" +learn_rel_path: "Integrations/Monitor/Mock Collectors" +--> + +# Example + +If you want to understand how charts.d data collector functions, check out the [charts.d example](https://raw.githubusercontent.com/netdata/netdata/master/src/collectors/charts.d.plugin/example/example.chart.sh). + + diff --git a/src/collectors/charts.d.plugin/example/example.chart.sh b/src/collectors/charts.d.plugin/example/example.chart.sh new file mode 100644 index 000000000..6bbbcf1d7 --- /dev/null +++ b/src/collectors/charts.d.plugin/example/example.chart.sh @@ -0,0 +1,123 @@ +# shellcheck shell=bash +# no need for shebang - this file is loaded from charts.d.plugin +# SPDX-License-Identifier: GPL-3.0-or-later + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# + +# if this chart is called X.chart.sh, then all functions and global variables +# must start with X_ + +# _update_every is a special variable - it holds the number of seconds +# between the calls of the _update() function +example_update_every= + +# the priority is used to sort the charts on the dashboard +# 1 = the first chart +example_priority=150000 + +# to enable this chart, you have to set this to 12345 +# (just a demonstration for something that needs to be checked) +example_magic_number= + +# global variables to store our collected data +# remember: they need to start with the module name example_ +example_value1= +example_value2= +example_value3= +example_value4= +example_last=0 +example_count=0 + +example_get() { + # do all the work to collect / calculate the values + # for each dimension + # + # Remember: + # 1. KEEP IT SIMPLE AND SHORT + # 2. AVOID FORKS (avoid piping commands) + # 3. AVOID CALLING TOO MANY EXTERNAL PROGRAMS + # 4. USE LOCAL VARIABLES (global variables may overlap with other modules) + + example_value1=$RANDOM + example_value2=$RANDOM + example_value3=$RANDOM + example_value4=$((8192 + (RANDOM * 16383 / 32767))) + + if [ $example_count -gt 0 ]; then + example_count=$((example_count - 1)) + + [ $example_last -gt 16383 ] && example_value4=$((example_last + (RANDOM * ((32767 - example_last) / 2) / 32767))) + [ $example_last -le 16383 ] && example_value4=$((example_last - (RANDOM * (example_last / 2) / 32767))) + else + example_count=$((1 + (RANDOM * 5 / 32767))) + + if [ $example_last -gt 16383 ] && [ $example_value4 -gt 16383 ]; then + example_value4=$((example_value4 - 16383)) + fi + if [ $example_last -le 16383 ] && [ $example_value4 -lt 16383 ]; then + example_value4=$((example_value4 + 16383)) + fi + fi + example_last=$example_value4 + + # this should return: + # - 0 to send the data to netdata + # - 1 to report a failure to collect the data + + return 0 +} + +# _check is called once, to find out if this chart should be enabled or not +example_check() { + # this should return: + # - 0 to enable the chart + # - 1 to disable the chart + + # check something + [ "${example_magic_number}" != "12345" ] && error "manual configuration required: you have to set example_magic_number=$example_magic_number in example.conf to start example chart." && return 1 + + # check that we can collect data + example_get || return 1 + + return 0 +} + +# _create is called once, to create the charts +example_create() { + # create the chart with 3 dimensions + cat << EOF +CHART example.random '' "Random Numbers Stacked Chart" "% of random numbers" random random stacked $((example_priority)) $example_update_every '' '' 'example' +DIMENSION random1 '' percentage-of-absolute-row 1 1 +DIMENSION random2 '' percentage-of-absolute-row 1 1 +DIMENSION random3 '' percentage-of-absolute-row 1 1 +CHART example.random2 '' "A random number" "random number" random random area $((example_priority + 1)) $example_update_every '' '' 'example' +DIMENSION random '' absolute 1 1 +EOF + + return 0 +} + +# _update is called continuously, to collect the values +example_update() { + # the first argument to this function is the microseconds since last update + # pass this parameter to the BEGIN statement (see below). + + example_get || return 1 + + # write the result of the work. + cat << VALUESEOF +BEGIN example.random $1 +SET random1 = $example_value1 +SET random2 = $example_value2 +SET random3 = $example_value3 +END +BEGIN example.random2 $1 +SET random = $example_value4 +END +VALUESEOF + + return 0 +} diff --git a/src/collectors/charts.d.plugin/example/example.conf b/src/collectors/charts.d.plugin/example/example.conf new file mode 100644 index 000000000..6232ca584 --- /dev/null +++ b/src/collectors/charts.d.plugin/example/example.conf @@ -0,0 +1,21 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2018 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +# to enable this chart, you have to set this to 12345 +# (just a demonstration for something that needs to be checked) +#example_magic_number=12345 + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#example_update_every= + +# the charts priority on the dashboard +#example_priority=150000 + +# the number of retries to do in case of failure +# before disabling the module +#example_retries=10 diff --git a/src/collectors/charts.d.plugin/libreswan/README.md b/src/collectors/charts.d.plugin/libreswan/README.md new file mode 120000 index 000000000..1416d9597 --- /dev/null +++ b/src/collectors/charts.d.plugin/libreswan/README.md @@ -0,0 +1 @@ +integrations/libreswan.md
\ No newline at end of file diff --git a/src/collectors/charts.d.plugin/libreswan/integrations/libreswan.md b/src/collectors/charts.d.plugin/libreswan/integrations/libreswan.md new file mode 100644 index 000000000..0b08e2b4f --- /dev/null +++ b/src/collectors/charts.d.plugin/libreswan/integrations/libreswan.md @@ -0,0 +1,194 @@ +<!--startmeta +custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/collectors/charts.d.plugin/libreswan/README.md" +meta_yaml: "https://github.com/netdata/netdata/edit/master/src/collectors/charts.d.plugin/libreswan/metadata.yaml" +sidebar_label: "Libreswan" +learn_status: "Published" +learn_rel_path: "Collecting Metrics/VPNs" +most_popular: False +message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE" +endmeta--> + +# Libreswan + + +<img src="https://netdata.cloud/img/libreswan.png" width="150"/> + + +Plugin: charts.d.plugin +Module: libreswan + +<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" /> + +## Overview + +Monitor Libreswan performance for optimal IPsec VPN operations. Improve your VPN operations with Netdata''s real-time metrics and built-in alerts. + +The collector uses the `ipsec` command to collect the information it needs. + +This collector is supported on all platforms. + +This collector supports collecting metrics from multiple instances of this integration, including remote instances. + + +### Default Behavior + +#### Auto-Detection + +This integration doesn't support auto-detection. + +#### Limits + +The default configuration for this integration does not impose any limits on data collection. + +#### Performance Impact + +The default configuration for this integration is not expected to impose a significant performance impact on the system. + + +## Metrics + +Metrics grouped by *scope*. + +The scope defines the instance that the metric belongs to. An instance is uniquely identified by a set of labels. + + + +### Per IPSEC tunnel + +Metrics related to IPSEC tunnels. Each tunnel provides its own set of the following metrics. + +This scope has no labels. + +Metrics: + +| Metric | Dimensions | Unit | +|:------|:----------|:----| +| libreswan.net | in, out | kilobits/s | +| libreswan.uptime | uptime | seconds | + + + +## Alerts + +There are no alerts configured by default for this integration. + + +## Setup + +### Prerequisites + +#### Install charts.d plugin + +If [using our official native DEB/RPM packages](https://github.com/netdata/netdata/blob/master/packaging/installer/UPDATE.md#determine-which-installation-method-you-used), make sure `netdata-plugin-chartsd` is installed. + + +#### Permissions to execute `ipsec` + +The plugin executes 2 commands to collect all the information it needs: + +```sh +ipsec whack --status +ipsec whack --trafficstatus +``` + +The first command is used to extract the currently established tunnels, their IDs and their names. +The second command is used to extract the current uptime and traffic. + +Most probably user `netdata` will not be able to query libreswan, so the `ipsec` commands will be denied. +The plugin attempts to run `ipsec` as `sudo ipsec ...`, to get access to libreswan statistics. + +To allow user `netdata` execute `sudo ipsec ...`, create the file `/etc/sudoers.d/netdata` with this content: + +``` +netdata ALL = (root) NOPASSWD: /sbin/ipsec whack --status +netdata ALL = (root) NOPASSWD: /sbin/ipsec whack --trafficstatus +``` + +Make sure the path `/sbin/ipsec` matches your setup (execute `which ipsec` to find the right path). + + + +### Configuration + +#### File + +The configuration file name for this integration is `charts.d/libreswan.conf`. + + +You can edit the configuration file using the `edit-config` script from the +Netdata [config directory](https://github.com/netdata/netdata/blob/master/docs/netdata-agent/configuration.md#the-netdata-config-directory). + +```bash +cd /etc/netdata 2>/dev/null || cd /opt/netdata/etc/netdata +sudo ./edit-config charts.d/libreswan.conf +``` +#### Options + +The config file is sourced by the charts.d plugin. It's a standard bash file. + +The following collapsed table contains all the options that can be configured for the libreswan collector. + + +<details><summary>Config options</summary> + +| Name | Description | Default | Required | +|:----|:-----------|:-------|:--------:| +| libreswan_update_every | The data collection frequency. If unset, will inherit the netdata update frequency. | 1 | no | +| libreswan_priority | The charts priority on the dashboard | 90000 | no | +| libreswan_retries | The number of retries to do in case of failure before disabling the collector. | 10 | no | +| libreswan_sudo | Whether to run `ipsec` with `sudo` or not. | 1 | no | + +</details> + +#### Examples + +##### Run `ipsec` without sudo + +Run the `ipsec` utility without sudo + +```yaml +# the data collection frequency +# if unset, will inherit the netdata update frequency +#libreswan_update_every=1 + +# the charts priority on the dashboard +#libreswan_priority=90000 + +# the number of retries to do in case of failure +# before disabling the module +#libreswan_retries=10 + +# set to 1, to run ipsec with sudo (the default) +# set to 0, to run ipsec without sudo +libreswan_sudo=0 + +``` + + +## Troubleshooting + +### Debug Mode + +To troubleshoot issues with the `libreswan` collector, run the `charts.d.plugin` with the debug option enabled. The output +should give you clues as to why the collector isn't working. + +- Navigate to the `plugins.d` directory, usually at `/usr/libexec/netdata/plugins.d/`. If that's not the case on + your system, open `netdata.conf` and look for the `plugins` setting under `[directories]`. + + ```bash + cd /usr/libexec/netdata/plugins.d/ + ``` + +- Switch to the `netdata` user. + + ```bash + sudo -u netdata -s + ``` + +- Run the `charts.d.plugin` to debug the collector: + + ```bash + ./charts.d.plugin debug 1 libreswan + ``` + + diff --git a/src/collectors/charts.d.plugin/libreswan/libreswan.chart.sh b/src/collectors/charts.d.plugin/libreswan/libreswan.chart.sh new file mode 100644 index 000000000..d526f7a91 --- /dev/null +++ b/src/collectors/charts.d.plugin/libreswan/libreswan.chart.sh @@ -0,0 +1,187 @@ +# shellcheck shell=bash disable=SC1117 +# no need for shebang - this file is loaded from charts.d.plugin +# SPDX-License-Identifier: GPL-3.0-or-later + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2018 Costa Tsaousis <costa@tsaousis.gr> +# + +# _update_every is a special variable - it holds the number of seconds +# between the calls of the _update() function +libreswan_update_every=1 + +# the priority is used to sort the charts on the dashboard +# 1 = the first chart +libreswan_priority=90000 + +# set to 1, to run ipsec with sudo +libreswan_sudo=1 + +# global variables to store our collected data + +# [TUNNELID] = TUNNELNAME +# here we track the *latest* established tunnels +# as detected by: ipsec whack --status +declare -A libreswan_connected_tunnels=() + +# [TUNNELID] = VALUE +# here we track values of all established tunnels (not only the latest) +# as detected by: ipsec whack --trafficstatus +declare -A libreswan_traffic_in=() +declare -A libreswan_traffic_out=() +declare -A libreswan_established_add_time=() + +# [TUNNELNAME] = CHARTID +# here we remember CHARTIDs of all tunnels +# we need this to avoid converting tunnel names to chart IDs on every iteration +declare -A libreswan_tunnel_charts=() + +is_able_sudo_ipsec() { + if ! sudo -n -l "${IPSEC_CMD}" whack --status > /dev/null 2>&1; then + return 1 + fi + if ! sudo -n -l "${IPSEC_CMD}" whack --trafficstatus > /dev/null 2>&1; then + return 1 + fi + return 0 +} + +# run the ipsec command +libreswan_ipsec() { + if [ ${libreswan_sudo} -ne 0 ]; then + sudo -n "${IPSEC_CMD}" "${@}" + return $? + else + "${IPSEC_CMD}" "${@}" + return $? + fi +} + +# fetch latest values - fill the arrays +libreswan_get() { + # do all the work to collect / calculate the values + # for each dimension + + # empty the variables + libreswan_traffic_in=() + libreswan_traffic_out=() + libreswan_established_add_time=() + libreswan_connected_tunnels=() + + # convert the ipsec command output to a shell script + # and source it to get the values + # shellcheck disable=SC1090 + source <( + { + libreswan_ipsec whack --status + libreswan_ipsec whack --trafficstatus + } | sed -n \ + -e "s|[0-9]\+ #\([0-9]\+\): \"\(.*\)\".*IPsec SA established.*newest IPSEC.*|libreswan_connected_tunnels[\"\1\"]=\"\2\"|p" \ + -e "s|[0-9]\+ #\([0-9]\+\): \"\(.*\)\",\{0,1\}.* add_time=\([0-9]\+\),.* inBytes=\([0-9]\+\),.* outBytes=\([0-9]\+\).*|libreswan_traffic_in[\"\1\"]=\"\4\"; libreswan_traffic_out[\"\1\"]=\"\5\"; libreswan_established_add_time[\"\1\"]=\"\3\";|p" + ) || return 1 + + # check we got some data + [ ${#libreswan_connected_tunnels[@]} -eq 0 ] && return 1 + + return 0 +} + +# _check is called once, to find out if this chart should be enabled or not +libreswan_check() { + # this should return: + # - 0 to enable the chart + # - 1 to disable the chart + + require_cmd ipsec || return 1 + + # make sure it is libreswan + # shellcheck disable=SC2143 + if [ -z "$(ipsec --version | grep -i libreswan)" ]; then + error "ipsec command is not Libreswan. Disabling Libreswan plugin." + return 1 + fi + + if [ ${libreswan_sudo} -ne 0 ] && ! is_able_sudo_ipsec; then + error "not enough permissions to execute ipsec with sudo. Disabling Libreswan plugin." + return 1 + fi + + # check that we can collect data + libreswan_get || return 1 + + return 0 +} + +# create the charts for an ipsec tunnel +libreswan_create_one() { + local n="${1}" name + + name="${libreswan_connected_tunnels[${n}]}" + + [ -n "${libreswan_tunnel_charts[${name}]}" ] && return 0 + + libreswan_tunnel_charts[${name}]="$(fixid "${name}")" + + cat << EOF +CHART libreswan.${libreswan_tunnel_charts[${name}]}_net '${name}_net' "LibreSWAN Tunnel ${name} Traffic" "kilobits/s" "${name}" libreswan.net area $((libreswan_priority)) $libreswan_update_every '' '' 'libreswan' +DIMENSION in '' incremental 8 1000 +DIMENSION out '' incremental -8 1000 +CHART libreswan.${libreswan_tunnel_charts[${name}]}_uptime '${name}_uptime' "LibreSWAN Tunnel ${name} Uptime" "seconds" "${name}" libreswan.uptime line $((libreswan_priority + 1)) $libreswan_update_every '' '' 'libreswan' +DIMENSION uptime '' absolute 1 1 +EOF + + return 0 + +} + +# _create is called once, to create the charts +libreswan_create() { + local n + for n in "${!libreswan_connected_tunnels[@]}"; do + libreswan_create_one "${n}" + done + return 0 +} + +libreswan_now=$(date +%s) + +# send the values to netdata for an ipsec tunnel +libreswan_update_one() { + local n="${1}" microseconds="${2}" name id uptime + + name="${libreswan_connected_tunnels[${n}]}" + id="${libreswan_tunnel_charts[${name}]}" + + [ -z "${id}" ] && libreswan_create_one "${name}" + + uptime=$((libreswan_now - libreswan_established_add_time[${n}])) + [ ${uptime} -lt 0 ] && uptime=0 + + # write the result of the work. + cat << VALUESEOF +BEGIN libreswan.${id}_net ${microseconds} +SET in = ${libreswan_traffic_in[${n}]} +SET out = ${libreswan_traffic_out[${n}]} +END +BEGIN libreswan.${id}_uptime ${microseconds} +SET uptime = ${uptime} +END +VALUESEOF +} + +# _update is called continuously, to collect the values +libreswan_update() { + # the first argument to this function is the microseconds since last update + # pass this parameter to the BEGIN statement (see below). + + libreswan_get || return 1 + libreswan_now=$(date +%s) + + local n + for n in "${!libreswan_connected_tunnels[@]}"; do + libreswan_update_one "${n}" "${@}" + done + + return 0 +} diff --git a/src/collectors/charts.d.plugin/libreswan/libreswan.conf b/src/collectors/charts.d.plugin/libreswan/libreswan.conf new file mode 100644 index 000000000..9b3ee77b7 --- /dev/null +++ b/src/collectors/charts.d.plugin/libreswan/libreswan.conf @@ -0,0 +1,29 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2018 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ +# + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#libreswan_update_every=1 + +# the charts priority on the dashboard +#libreswan_priority=90000 + +# the number of retries to do in case of failure +# before disabling the module +#libreswan_retries=10 + +# set to 1, to run ipsec with sudo (the default) +# set to 0, to run ipsec without sudo +#libreswan_sudo=1 + +# TO ALLOW NETDATA RUN ipsec AS ROOT +# CREATE THE FILE: /etc/sudoers.d/netdata +# WITH THESE 2 LINES (uncommented of course): +# +# netdata ALL = (root) NOPASSWD: /sbin/ipsec whack --status +# netdata ALL = (root) NOPASSWD: /sbin/ipsec whack --trafficstatus diff --git a/src/collectors/charts.d.plugin/libreswan/metadata.yaml b/src/collectors/charts.d.plugin/libreswan/metadata.yaml new file mode 100644 index 000000000..77cb25450 --- /dev/null +++ b/src/collectors/charts.d.plugin/libreswan/metadata.yaml @@ -0,0 +1,146 @@ +plugin_name: charts.d.plugin +modules: + - meta: + plugin_name: charts.d.plugin + module_name: libreswan + monitored_instance: + name: Libreswan + link: "https://libreswan.org/" + categories: + - data-collection.vpns + icon_filename: "libreswan.png" + related_resources: + integrations: + list: [] + info_provided_to_referring_integrations: + description: "" + keywords: + - vpn + - libreswan + - network + - ipsec + most_popular: false + overview: + data_collection: + metrics_description: "Monitor Libreswan performance for optimal IPsec VPN operations. Improve your VPN operations with Netdata''s real-time metrics and built-in alerts." + method_description: "The collector uses the `ipsec` command to collect the information it needs." + supported_platforms: + include: [] + exclude: [] + multi_instance: true + additional_permissions: + description: "" + default_behavior: + auto_detection: + description: "" + limits: + description: "" + performance_impact: + description: "" + setup: + prerequisites: + list: + - title: "Install charts.d plugin" + description: | + If [using our official native DEB/RPM packages](https://github.com/netdata/netdata/blob/master/packaging/installer/UPDATE.md#determine-which-installation-method-you-used), make sure `netdata-plugin-chartsd` is installed. + - title: "Permissions to execute `ipsec`" + description: | + The plugin executes 2 commands to collect all the information it needs: + + ```sh + ipsec whack --status + ipsec whack --trafficstatus + ``` + + The first command is used to extract the currently established tunnels, their IDs and their names. + The second command is used to extract the current uptime and traffic. + + Most probably user `netdata` will not be able to query libreswan, so the `ipsec` commands will be denied. + The plugin attempts to run `ipsec` as `sudo ipsec ...`, to get access to libreswan statistics. + + To allow user `netdata` execute `sudo ipsec ...`, create the file `/etc/sudoers.d/netdata` with this content: + + ``` + netdata ALL = (root) NOPASSWD: /sbin/ipsec whack --status + netdata ALL = (root) NOPASSWD: /sbin/ipsec whack --trafficstatus + ``` + + Make sure the path `/sbin/ipsec` matches your setup (execute `which ipsec` to find the right path). + configuration: + file: + name: charts.d/libreswan.conf + options: + description: | + The config file is sourced by the charts.d plugin. It's a standard bash file. + + The following collapsed table contains all the options that can be configured for the libreswan collector. + folding: + title: "Config options" + enabled: true + list: + - name: libreswan_update_every + description: The data collection frequency. If unset, will inherit the netdata update frequency. + default_value: 1 + required: false + - name: libreswan_priority + description: The charts priority on the dashboard + default_value: 90000 + required: false + - name: libreswan_retries + description: The number of retries to do in case of failure before disabling the collector. + default_value: 10 + required: false + - name: libreswan_sudo + description: Whether to run `ipsec` with `sudo` or not. + default_value: 1 + required: false + examples: + folding: + enabled: false + title: "Config" + list: + - name: Run `ipsec` without sudo + description: Run the `ipsec` utility without sudo + config: | + # the data collection frequency + # if unset, will inherit the netdata update frequency + #libreswan_update_every=1 + + # the charts priority on the dashboard + #libreswan_priority=90000 + + # the number of retries to do in case of failure + # before disabling the module + #libreswan_retries=10 + + # set to 1, to run ipsec with sudo (the default) + # set to 0, to run ipsec without sudo + libreswan_sudo=0 + troubleshooting: + problems: + list: [] + alerts: [] + metrics: + folding: + title: Metrics + enabled: false + description: "" + availability: [] + scopes: + - name: IPSEC tunnel + description: "Metrics related to IPSEC tunnels. Each tunnel provides its own set of the following metrics." + labels: [] + metrics: + - name: libreswan.net + description: LibreSWAN Tunnel ${name} Traffic + unit: "kilobits/s" + chart_type: area + dimensions: + - name: in + - name: out + - name: libreswan.uptime + description: LibreSWAN Tunnel ${name} Uptime + unit: "seconds" + chart_type: line + dimensions: + - name: uptime diff --git a/src/collectors/charts.d.plugin/loopsleepms.sh.inc b/src/collectors/charts.d.plugin/loopsleepms.sh.inc new file mode 100644 index 000000000..5608b8d8f --- /dev/null +++ b/src/collectors/charts.d.plugin/loopsleepms.sh.inc @@ -0,0 +1,227 @@ +# no need for shebang - this file is included from other scripts +# SPDX-License-Identifier: GPL-3.0-or-later + +LOOPSLEEP_DATE="$(which date 2>/dev/null || command -v date 2>/dev/null)" +if [ -z "$LOOPSLEEP_DATE" ]; then + echo >&2 "$0: ERROR: Cannot find the command 'date' in the system path." + exit 1 +fi + +# ----------------------------------------------------------------------------- +# use the date command as a high resolution timer + +# macOS 'date' doesn't support '%N' precision +# echo $(/bin/date +"%N") is "N" +if [ "$($LOOPSLEEP_DATE +"%N")" = "N" ]; then + LOOPSLEEP_DATE_FORMAT="%s * 1000" +else + LOOPSLEEP_DATE_FORMAT="%s * 1000 + 10#%-N / 1000000" +fi + +now_ms= +LOOPSLEEPMS_HIGHRES=1 +test "$($LOOPSLEEP_DATE +%N)" = "%N" && LOOPSLEEPMS_HIGHRES=0 +test -z "$($LOOPSLEEP_DATE +%N)" && LOOPSLEEPMS_HIGHRES=0 +current_time_ms_from_date() { + if [ $LOOPSLEEPMS_HIGHRES -eq 0 ]; then + now_ms="$($LOOPSLEEP_DATE +'%s')000" + else + now_ms="$(($($LOOPSLEEP_DATE +"$LOOPSLEEP_DATE_FORMAT")))" + fi +} + +# ----------------------------------------------------------------------------- +# use /proc/uptime as a high resolution timer + +current_time_ms_from_date +current_time_ms_from_uptime_started="${now_ms}" +current_time_ms_from_uptime_last="${now_ms}" +current_time_ms_from_uptime_first=0 +current_time_ms_from_uptime() { + local up rest arr=() n + + read up rest </proc/uptime + if [ $? -ne 0 ]; then + echo >&2 "$0: Cannot read /proc/uptime - falling back to current_time_ms_from_date()." + current_time_ms="current_time_ms_from_date" + current_time_ms_from_date + current_time_ms_accuracy=1 + return + fi + + arr=(${up//./ }) + + if [ ${#arr[1]} -lt 1 ]; then + n="${arr[0]}000" + elif [ ${#arr[1]} -lt 2 ]; then + n="${arr[0]}${arr[1]}00" + elif [ ${#arr[1]} -lt 3 ]; then + n="${arr[0]}${arr[1]}0" + else + n="${arr[0]}${arr[1]}" + fi + + now_ms=$((current_time_ms_from_uptime_started - current_time_ms_from_uptime_first + n)) + + if [ "${now_ms}" -lt "${current_time_ms_from_uptime_last}" ]; then + echo >&2 "$0: Cannot use current_time_ms_from_uptime() - new time ${now_ms} is older than the last ${current_time_ms_from_uptime_last} - falling back to current_time_ms_from_date()." + current_time_ms="current_time_ms_from_date" + current_time_ms_from_date + current_time_ms_accuracy=1 + fi + + current_time_ms_from_uptime_last="${now_ms}" +} +current_time_ms_from_uptime +current_time_ms_from_uptime_first="$((now_ms - current_time_ms_from_uptime_started))" +current_time_ms_from_uptime_last="${current_time_ms_from_uptime_first}" +current_time_ms="current_time_ms_from_uptime" +current_time_ms_accuracy=10 +if [ "${current_time_ms_from_uptime_first}" -eq 0 ]; then + echo >&2 "$0: Invalid setup for current_time_ms_from_uptime() - falling back to current_time_ms_from_date()." + current_time_ms="current_time_ms_from_date" + current_time_ms_accuracy=1 +fi + +# ----------------------------------------------------------------------------- +# use read with timeout for sleep + +mysleep="" + +mysleep_fifo="${NETDATA_CACHE_DIR-/tmp}/.netdata_bash_sleep_timer_fifo" +[ -f "${mysleep_fifo}" ] && rm "${mysleep_fifo}" +[ ! -p "${mysleep_fifo}" ] && mkfifo "${mysleep_fifo}" +[ -p "${mysleep_fifo}" ] && mysleep="mysleep_read" + +mysleep_read() { + read -t "${1}" <>"${mysleep_fifo}" + ret=$? + if [ $ret -le 128 ]; then + echo >&2 "$0: Cannot use read for sleeping (return code ${ret})." + mysleep="sleep" + ${mysleep} "${1}" + fi +} + +# ----------------------------------------------------------------------------- +# use bash loadable module for sleep + +mysleep_builtin() { + builtin sleep "${1}" + ret=$? + if [ $ret -ne 0 ]; then + echo >&2 "$0: Cannot use builtin sleep for sleeping (return code ${ret})." + mysleep="sleep" + ${mysleep} "${1}" + fi +} + +if [ -z "${mysleep}" -a "$((BASH_VERSINFO[0] + 0))" -ge 3 -a "${NETDATA_BASH_LOADABLES}" != "DISABLE" ]; then + # enable modules only for bash version 3+ + + for bash_modules_path in ${BASH_LOADABLES_PATH//:/ } "$(pkg-config bash --variable=loadablesdir 2>/dev/null)" "/usr/lib/bash" "/lib/bash" "/lib64/bash" "/usr/local/lib/bash" "/usr/local/lib64/bash"; do + [ -z "${bash_modules_path}" -o ! -d "${bash_modules_path}" ] && continue + + # check for sleep + for bash_module_sleep in "sleep" "sleep.so"; do + if [ -f "${bash_modules_path}/${bash_module_sleep}" ]; then + if enable -f "${bash_modules_path}/${bash_module_sleep}" sleep 2>/dev/null; then + mysleep="mysleep_builtin" + # echo >&2 "$0: Using bash loadable ${bash_modules_path}/${bash_module_sleep} for sleep" + break + fi + fi + + done + + [ ! -z "${mysleep}" ] && break + done +fi + +# ----------------------------------------------------------------------------- +# fallback to external sleep + +[ -z "${mysleep}" ] && mysleep="sleep" + +# ----------------------------------------------------------------------------- +# this function is used to sleep a fraction of a second +# it calculates the difference between every time is called +# and tries to align the sleep time to give you exactly the +# loop you need. + +LOOPSLEEPMS_LASTRUN=0 +LOOPSLEEPMS_NEXTRUN=0 +LOOPSLEEPMS_LASTSLEEP=0 +LOOPSLEEPMS_LASTWORK=0 + +loopsleepms() { + local tellwork=0 t="${1}" div s m now mstosleep + + if [ "${t}" = "tellwork" ]; then + tellwork=1 + shift + t="${1}" + fi + + # $t = the time in seconds to wait + + # if high resolution is not supported + # just sleep the time requested, in seconds + if [ ${LOOPSLEEPMS_HIGHRES} -eq 0 ]; then + sleep ${t} + return + fi + + # get the current time, in ms in ${now_ms} + ${current_time_ms} + + # calculate ms since last run + [ ${LOOPSLEEPMS_LASTRUN} -gt 0 ] && + LOOPSLEEPMS_LASTWORK=$((now_ms - LOOPSLEEPMS_LASTRUN - LOOPSLEEPMS_LASTSLEEP + current_time_ms_accuracy)) + # echo "# last loop's work took $LOOPSLEEPMS_LASTWORK ms" + + # remember this run + LOOPSLEEPMS_LASTRUN=${now_ms} + + # calculate the next run + LOOPSLEEPMS_NEXTRUN=$(((now_ms - (now_ms % (t * 1000))) + (t * 1000))) + + # calculate ms to sleep + mstosleep=$((LOOPSLEEPMS_NEXTRUN - now_ms + current_time_ms_accuracy)) + # echo "# mstosleep is $mstosleep ms" + + # if we are too slow, sleep some time + test ${mstosleep} -lt 200 && mstosleep=200 + + s=$((mstosleep / 1000)) + m=$((mstosleep - (s * 1000))) + [ "${m}" -lt 100 ] && m="0${m}" + [ "${m}" -lt 10 ] && m="0${m}" + + test $tellwork -eq 1 && echo >&2 " >>> PERFORMANCE >>> WORK TOOK ${LOOPSLEEPMS_LASTWORK} ms ( $((LOOPSLEEPMS_LASTWORK * 100 / 1000)).$((LOOPSLEEPMS_LASTWORK % 10))% cpu ) >>> SLEEPING ${mstosleep} ms" + + # echo "# sleeping ${s}.${m}" + # echo + ${mysleep} ${s}.${m} + + # keep the values we need + # for our next run + LOOPSLEEPMS_LASTSLEEP=$mstosleep +} + +# test it +#while [ 1 ] +#do +# r=$(( (RANDOM * 2000 / 32767) )) +# s=$((r / 1000)) +# m=$((r - (s * 1000))) +# [ "${m}" -lt 100 ] && m="0${m}" +# [ "${m}" -lt 10 ] && m="0${m}" +# echo "${r} = ${s}.${m}" +# +# # the work +# ${mysleep} ${s}.${m} +# +# # the alignment loop +# loopsleepms tellwork 1 +#done diff --git a/src/collectors/charts.d.plugin/opensips/README.md b/src/collectors/charts.d.plugin/opensips/README.md new file mode 120000 index 000000000..bb85ba6d0 --- /dev/null +++ b/src/collectors/charts.d.plugin/opensips/README.md @@ -0,0 +1 @@ +integrations/opensips.md
\ No newline at end of file diff --git a/src/collectors/charts.d.plugin/opensips/integrations/opensips.md b/src/collectors/charts.d.plugin/opensips/integrations/opensips.md new file mode 100644 index 000000000..6a6a309e8 --- /dev/null +++ b/src/collectors/charts.d.plugin/opensips/integrations/opensips.md @@ -0,0 +1,192 @@ +<!--startmeta +custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/collectors/charts.d.plugin/opensips/README.md" +meta_yaml: "https://github.com/netdata/netdata/edit/master/src/collectors/charts.d.plugin/opensips/metadata.yaml" +sidebar_label: "OpenSIPS" +learn_status: "Published" +learn_rel_path: "Collecting Metrics/Telephony Servers" +most_popular: False +message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE" +endmeta--> + +# OpenSIPS + + +<img src="https://netdata.cloud/img/opensips.png" width="150"/> + + +Plugin: charts.d.plugin +Module: opensips + +<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" /> + +## Overview + +Examine OpenSIPS metrics for insights into SIP server operations. Study call rates, error rates, and response times for reliable voice over IP services. + +The collector uses the `opensipsctl` command line utility to gather OpenSIPS metrics. + +This collector is supported on all platforms. + +This collector supports collecting metrics from multiple instances of this integration, including remote instances. + + +### Default Behavior + +#### Auto-Detection + +The collector will attempt to call `opensipsctl` along with a default number of parameters, even without any configuration. + +#### Limits + +The default configuration for this integration does not impose any limits on data collection. + +#### Performance Impact + +The default configuration for this integration is not expected to impose a significant performance impact on the system. + + +## Metrics + +Metrics grouped by *scope*. + +The scope defines the instance that the metric belongs to. An instance is uniquely identified by a set of labels. + + + +### Per OpenSIPS instance + +These metrics refer to the entire monitored application. + +This scope has no labels. + +Metrics: + +| Metric | Dimensions | Unit | +|:------|:----------|:----| +| opensips.dialogs_active | active, early | dialogs | +| opensips.users | registered, location, contacts, expires | users | +| opensips.registrar | accepted, rejected | registrations/s | +| opensips.transactions | UAS, UAC | transactions/s | +| opensips.core_rcv | requests, replies | queries/s | +| opensips.core_fwd | requests, replies | queries/s | +| opensips.core_drop | requests, replies | queries/s | +| opensips.core_err | requests, replies | queries/s | +| opensips.core_bad | bad_URIs_rcvd, unsupported_methods, bad_msg_hdr | queries/s | +| opensips.tm_replies | received, relayed, local | replies/s | +| opensips.transactions_status | 2xx, 3xx, 4xx, 5xx, 6xx | transactions/s | +| opensips.transactions_inuse | inuse | transactions | +| opensips.sl_replies | 1xx, 2xx, 3xx, 4xx, 5xx, 6xx, sent, error, ACKed | replies/s | +| opensips.dialogs | processed, expire, failed | dialogs/s | +| opensips.net_waiting | UDP, TCP | kilobytes | +| opensips.uri_checks | positive, negative | checks / sec | +| opensips.traces | requests, replies | traces / sec | +| opensips.shmem | total, used, real_used, max_used, free | kilobytes | +| opensips.shmem_fragment | fragments | fragments | + + + +## Alerts + +There are no alerts configured by default for this integration. + + +## Setup + +### Prerequisites + +#### Install charts.d plugin + +If [using our official native DEB/RPM packages](https://github.com/netdata/netdata/blob/master/packaging/installer/UPDATE.md#determine-which-installation-method-you-used), make sure `netdata-plugin-chartsd` is installed. + + +#### Required software + +The collector requires the `opensipsctl` to be installed. + + +### Configuration + +#### File + +The configuration file name for this integration is `charts.d/opensips.conf`. + + +You can edit the configuration file using the `edit-config` script from the +Netdata [config directory](https://github.com/netdata/netdata/blob/master/docs/netdata-agent/configuration.md#the-netdata-config-directory). + +```bash +cd /etc/netdata 2>/dev/null || cd /opt/netdata/etc/netdata +sudo ./edit-config charts.d/opensips.conf +``` +#### Options + +The config file is sourced by the charts.d plugin. It's a standard bash file. + +The following collapsed table contains all the options that can be configured for the opensips collector. + + +<details><summary>Config options</summary> + +| Name | Description | Default | Required | +|:----|:-----------|:-------|:--------:| +| opensips_opts | Specify parameters to the `opensipsctl` command. If the default value fails to get global status, set here whatever options are needed to connect to the opensips server. | fifo get_statistics all | no | +| opensips_cmd | If `opensipsctl` is not in $PATH, specify it's full path here. | | no | +| opensips_timeout | How long to wait for `opensipsctl` to respond. | 2 | no | +| opensips_update_every | The data collection frequency. If unset, will inherit the netdata update frequency. | 5 | no | +| opensips_priority | The charts priority on the dashboard. | 80000 | no | +| opensips_retries | The number of retries to do in case of failure before disabling the collector. | 10 | no | + +</details> + +#### Examples + +##### Custom `opensipsctl` command + +Set a custom path to the `opensipsctl` command + +```yaml +#opensips_opts="fifo get_statistics all" +opensips_cmd=/opt/opensips/bin/opensipsctl +#opensips_timeout=2 + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#opensips_update_every=5 + +# the charts priority on the dashboard +#opensips_priority=80000 + +# the number of retries to do in case of failure +# before disabling the module +#opensips_retries=10 + +``` + + +## Troubleshooting + +### Debug Mode + +To troubleshoot issues with the `opensips` collector, run the `charts.d.plugin` with the debug option enabled. The output +should give you clues as to why the collector isn't working. + +- Navigate to the `plugins.d` directory, usually at `/usr/libexec/netdata/plugins.d/`. If that's not the case on + your system, open `netdata.conf` and look for the `plugins` setting under `[directories]`. + + ```bash + cd /usr/libexec/netdata/plugins.d/ + ``` + +- Switch to the `netdata` user. + + ```bash + sudo -u netdata -s + ``` + +- Run the `charts.d.plugin` to debug the collector: + + ```bash + ./charts.d.plugin debug 1 opensips + ``` + + diff --git a/src/collectors/charts.d.plugin/opensips/metadata.yaml b/src/collectors/charts.d.plugin/opensips/metadata.yaml new file mode 100644 index 000000000..356de5615 --- /dev/null +++ b/src/collectors/charts.d.plugin/opensips/metadata.yaml @@ -0,0 +1,270 @@ +plugin_name: charts.d.plugin +modules: + - meta: + plugin_name: charts.d.plugin + module_name: opensips + monitored_instance: + name: OpenSIPS + link: "https://opensips.org/" + categories: + - data-collection.telephony-servers + icon_filename: "opensips.png" + related_resources: + integrations: + list: [] + info_provided_to_referring_integrations: + description: "" + keywords: + - opensips + - sip + - voice + - video + - stream + most_popular: false + overview: + data_collection: + metrics_description: "Examine OpenSIPS metrics for insights into SIP server operations. Study call rates, error rates, and response times for reliable voice over IP services." + method_description: "The collector uses the `opensipsctl` command line utility to gather OpenSIPS metrics." + supported_platforms: + include: [] + exclude: [] + multi_instance: true + additional_permissions: + description: "" + default_behavior: + auto_detection: + description: "The collector will attempt to call `opensipsctl` along with a default number of parameters, even without any configuration." + limits: + description: "" + performance_impact: + description: "" + setup: + prerequisites: + list: + - title: "Install charts.d plugin" + description: | + If [using our official native DEB/RPM packages](https://github.com/netdata/netdata/blob/master/packaging/installer/UPDATE.md#determine-which-installation-method-you-used), make sure `netdata-plugin-chartsd` is installed. + - title: "Required software" + description: "The collector requires the `opensipsctl` to be installed." + configuration: + file: + name: charts.d/opensips.conf + options: + description: | + The config file is sourced by the charts.d plugin. It's a standard bash file. + + The following collapsed table contains all the options that can be configured for the opensips collector. + folding: + title: "Config options" + enabled: true + list: + - name: opensips_opts + description: Specify parameters to the `opensipsctl` command. If the default value fails to get global status, set here whatever options are needed to connect to the opensips server. + default_value: "fifo get_statistics all" + required: false + - name: opensips_cmd + description: If `opensipsctl` is not in $PATH, specify it's full path here. + default_value: "" + required: false + - name: opensips_timeout + description: How long to wait for `opensipsctl` to respond. + default_value: 2 + required: false + - name: opensips_update_every + description: The data collection frequency. If unset, will inherit the netdata update frequency. + default_value: 5 + required: false + - name: opensips_priority + description: The charts priority on the dashboard. + default_value: 80000 + required: false + - name: opensips_retries + description: The number of retries to do in case of failure before disabling the collector. + default_value: 10 + required: false + examples: + folding: + enabled: false + title: "Config" + list: + - name: Custom `opensipsctl` command + description: Set a custom path to the `opensipsctl` command + config: | + #opensips_opts="fifo get_statistics all" + opensips_cmd=/opt/opensips/bin/opensipsctl + #opensips_timeout=2 + + # the data collection frequency + # if unset, will inherit the netdata update frequency + #opensips_update_every=5 + + # the charts priority on the dashboard + #opensips_priority=80000 + + # the number of retries to do in case of failure + # before disabling the module + #opensips_retries=10 + troubleshooting: + problems: + list: [] + alerts: [] + metrics: + folding: + title: Metrics + enabled: false + description: "" + availability: [] + scopes: + - name: global + description: "These metrics refer to the entire monitored application." + labels: [] + metrics: + - name: opensips.dialogs_active + description: OpenSIPS Active Dialogs + unit: "dialogs" + chart_type: area + dimensions: + - name: active + - name: early + - name: opensips.users + description: OpenSIPS Users + unit: "users" + chart_type: line + dimensions: + - name: registered + - name: location + - name: contacts + - name: expires + - name: opensips.registrar + description: OpenSIPS Registrar + unit: "registrations/s" + chart_type: line + dimensions: + - name: accepted + - name: rejected + - name: opensips.transactions + description: OpenSIPS Transactions + unit: "transactions/s" + chart_type: line + dimensions: + - name: UAS + - name: UAC + - name: opensips.core_rcv + description: OpenSIPS Core Receives + unit: "queries/s" + chart_type: line + dimensions: + - name: requests + - name: replies + - name: opensips.core_fwd + description: OpenSIPS Core Forwards + unit: "queries/s" + chart_type: line + dimensions: + - name: requests + - name: replies + - name: opensips.core_drop + description: OpenSIPS Core Drops + unit: "queries/s" + chart_type: line + dimensions: + - name: requests + - name: replies + - name: opensips.core_err + description: OpenSIPS Core Errors + unit: "queries/s" + chart_type: line + dimensions: + - name: requests + - name: replies + - name: opensips.core_bad + description: OpenSIPS Core Bad + unit: "queries/s" + chart_type: line + dimensions: + - name: bad_URIs_rcvd + - name: unsupported_methods + - name: bad_msg_hdr + - name: opensips.tm_replies + description: OpenSIPS TM Replies + unit: "replies/s" + chart_type: line + dimensions: + - name: received + - name: relayed + - name: local + - name: opensips.transactions_status + description: OpenSIPS Transactions Status + unit: "transactions/s" + chart_type: line + dimensions: + - name: 2xx + - name: 3xx + - name: 4xx + - name: 5xx + - name: 6xx + - name: opensips.transactions_inuse + description: OpenSIPS InUse Transactions + unit: "transactions" + chart_type: line + dimensions: + - name: inuse + - name: opensips.sl_replies + description: OpenSIPS SL Replies + unit: "replies/s" + chart_type: line + dimensions: + - name: 1xx + - name: 2xx + - name: 3xx + - name: 4xx + - name: 5xx + - name: 6xx + - name: sent + - name: error + - name: ACKed + - name: opensips.dialogs + description: OpenSIPS Dialogs + unit: "dialogs/s" + chart_type: line + dimensions: + - name: processed + - name: expire + - name: failed + - name: opensips.net_waiting + description: OpenSIPS Network Waiting + unit: "kilobytes" + chart_type: line + dimensions: + - name: UDP + - name: TCP + - name: opensips.uri_checks + description: OpenSIPS URI Checks + unit: "checks / sec" + chart_type: line + dimensions: + - name: positive + - name: negative + - name: opensips.traces + description: OpenSIPS Traces + unit: "traces / sec" + chart_type: line + dimensions: + - name: requests + - name: replies + - name: opensips.shmem + description: OpenSIPS Shared Memory + unit: "kilobytes" + chart_type: line + dimensions: + - name: total + - name: used + - name: real_used + - name: max_used + - name: free + - name: opensips.shmem_fragment + description: OpenSIPS Shared Memory Fragmentation + unit: "fragments" + chart_type: line + dimensions: + - name: fragments diff --git a/src/collectors/charts.d.plugin/opensips/opensips.chart.sh b/src/collectors/charts.d.plugin/opensips/opensips.chart.sh new file mode 100644 index 000000000..02401fd59 --- /dev/null +++ b/src/collectors/charts.d.plugin/opensips/opensips.chart.sh @@ -0,0 +1,325 @@ +# shellcheck shell=bash disable=SC1117,SC2154,SC2086 +# no need for shebang - this file is loaded from charts.d.plugin +# SPDX-License-Identifier: GPL-3.0-or-later + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# + +opensips_opts="fifo get_statistics all" +opensips_cmd= +opensips_update_every=5 +opensips_timeout=2 +opensips_priority=80000 + +opensips_get_stats() { + run -t $opensips_timeout "$opensips_cmd" $opensips_opts | + grep "^\(core\|dialog\|net\|registrar\|shmem\|siptrace\|sl\|tm\|uri\|usrloc\):[a-zA-Z0-9_-]\+[[:space:]]*[=:]\+[[:space:]]*[0-9]\+[[:space:]]*$" | + sed \ + -e "s|[[:space:]]*[=:]\+[[:space:]]*\([0-9]\+\)[[:space:]]*$|=\1|g" \ + -e "s|[[:space:]:-]\+|_|g" \ + -e "s|^|opensips_|g" + + local ret=$? + [ $ret -ne 0 ] && echo "opensips_command_failed=1" + return $ret +} + +opensips_check() { + # if the user did not provide an opensips_cmd + # try to find it in the system + if [ -z "$opensips_cmd" ]; then + require_cmd opensipsctl || return 1 + opensips_cmd="$OPENSIPSCTL_CMD" + fi + + # check once if the command works + local x + x="$(opensips_get_stats | grep "^opensips_core_")" + # shellcheck disable=SC2181 + if [ ! $? -eq 0 ] || [ -z "$x" ]; then + error "cannot get global status. Please set opensips_opts='options' whatever needed to get connected to opensips server, in $confd/opensips.conf" + return 1 + fi + + return 0 +} + +opensips_create() { + # create the charts + cat << EOF +CHART opensips.dialogs_active '' "OpenSIPS Active Dialogs" "dialogs" dialogs '' area $((opensips_priority + 1)) $opensips_update_every '' '' 'opensips' +DIMENSION dialog_active_dialogs active absolute 1 1 +DIMENSION dialog_early_dialogs early absolute -1 1 + +CHART opensips.users '' "OpenSIPS Users" "users" users '' line $((opensips_priority + 2)) $opensips_update_every '' '' 'opensips' +DIMENSION usrloc_registered_users registered absolute 1 1 +DIMENSION usrloc_location_users location absolute 1 1 +DIMENSION usrloc_location_contacts contacts absolute 1 1 +DIMENSION usrloc_location_expires expires incremental -1 1 + +CHART opensips.registrar '' "OpenSIPS Registrar" "registrations/s" registrar '' line $((opensips_priority + 3)) $opensips_update_every '' '' 'opensips' +DIMENSION registrar_accepted_regs accepted incremental 1 1 +DIMENSION registrar_rejected_regs rejected incremental -1 1 + +CHART opensips.transactions '' "OpenSIPS Transactions" "transactions/s" transactions '' line $((opensips_priority + 4)) $opensips_update_every '' '' 'opensips' +DIMENSION tm_UAS_transactions UAS incremental 1 1 +DIMENSION tm_UAC_transactions UAC incremental -1 1 + +CHART opensips.core_rcv '' "OpenSIPS Core Receives" "queries/s" core '' line $((opensips_priority + 5)) $opensips_update_every '' '' 'opensips' +DIMENSION core_rcv_requests requests incremental 1 1 +DIMENSION core_rcv_replies replies incremental -1 1 + +CHART opensips.core_fwd '' "OpenSIPS Core Forwards" "queries/s" core '' line $((opensips_priority + 6)) $opensips_update_every '' '' 'opensips' +DIMENSION core_fwd_requests requests incremental 1 1 +DIMENSION core_fwd_replies replies incremental -1 1 + +CHART opensips.core_drop '' "OpenSIPS Core Drops" "queries/s" core '' line $((opensips_priority + 7)) $opensips_update_every '' '' 'opensips' +DIMENSION core_drop_requests requests incremental 1 1 +DIMENSION core_drop_replies replies incremental -1 1 + +CHART opensips.core_err '' "OpenSIPS Core Errors" "queries/s" core '' line $((opensips_priority + 8)) $opensips_update_every '' '' 'opensips' +DIMENSION core_err_requests requests incremental 1 1 +DIMENSION core_err_replies replies incremental -1 1 + +CHART opensips.core_bad '' "OpenSIPS Core Bad" "queries/s" core '' line $((opensips_priority + 9)) $opensips_update_every '' '' 'opensips' +DIMENSION core_bad_URIs_rcvd bad_URIs_rcvd incremental 1 1 +DIMENSION core_unsupported_methods unsupported_methods incremental 1 1 +DIMENSION core_bad_msg_hdr bad_msg_hdr incremental 1 1 + +CHART opensips.tm_replies '' "OpenSIPS TM Replies" "replies/s" transactions '' line $((opensips_priority + 10)) $opensips_update_every '' '' 'opensips' +DIMENSION tm_received_replies received incremental 1 1 +DIMENSION tm_relayed_replies relayed incremental 1 1 +DIMENSION tm_local_replies local incremental 1 1 + +CHART opensips.transactions_status '' "OpenSIPS Transactions Status" "transactions/s" transactions '' line $((opensips_priority + 11)) $opensips_update_every '' '' 'opensips' +DIMENSION tm_2xx_transactions 2xx incremental 1 1 +DIMENSION tm_3xx_transactions 3xx incremental 1 1 +DIMENSION tm_4xx_transactions 4xx incremental 1 1 +DIMENSION tm_5xx_transactions 5xx incremental 1 1 +DIMENSION tm_6xx_transactions 6xx incremental 1 1 + +CHART opensips.transactions_inuse '' "OpenSIPS InUse Transactions" "transactions" transactions '' line $((opensips_priority + 12)) $opensips_update_every '' '' 'opensips' +DIMENSION tm_inuse_transactions inuse absolute 1 1 + +CHART opensips.sl_replies '' "OpenSIPS SL Replies" "replies/s" core '' line $((opensips_priority + 13)) $opensips_update_every '' '' 'opensips' +DIMENSION sl_1xx_replies 1xx incremental 1 1 +DIMENSION sl_2xx_replies 2xx incremental 1 1 +DIMENSION sl_3xx_replies 3xx incremental 1 1 +DIMENSION sl_4xx_replies 4xx incremental 1 1 +DIMENSION sl_5xx_replies 5xx incremental 1 1 +DIMENSION sl_6xx_replies 6xx incremental 1 1 +DIMENSION sl_sent_replies sent incremental 1 1 +DIMENSION sl_sent_err_replies error incremental 1 1 +DIMENSION sl_received_ACKs ACKed incremental 1 1 + +CHART opensips.dialogs '' "OpenSIPS Dialogs" "dialogs/s" dialogs '' line $((opensips_priority + 14)) $opensips_update_every '' '' 'opensips' +DIMENSION dialog_processed_dialogs processed incremental 1 1 +DIMENSION dialog_expired_dialogs expired incremental 1 1 +DIMENSION dialog_failed_dialogs failed incremental -1 1 + +CHART opensips.net_waiting '' "OpenSIPS Network Waiting" "kilobytes" net '' line $((opensips_priority + 15)) $opensips_update_every '' '' 'opensips' +DIMENSION net_waiting_udp UDP absolute 1 1024 +DIMENSION net_waiting_tcp TCP absolute 1 1024 + +CHART opensips.uri_checks '' "OpenSIPS URI Checks" "checks / sec" uri '' line $((opensips_priority + 16)) $opensips_update_every '' '' 'opensips' +DIMENSION uri_positive_checks positive incremental 1 1 +DIMENSION uri_negative_checks negative incremental -1 1 + +CHART opensips.traces '' "OpenSIPS Traces" "traces / sec" traces '' line $((opensips_priority + 17)) $opensips_update_every '' '' 'opensips' +DIMENSION siptrace_traced_requests requests incremental 1 1 +DIMENSION siptrace_traced_replies replies incremental -1 1 + +CHART opensips.shmem '' "OpenSIPS Shared Memory" "kilobytes" mem '' line $((opensips_priority + 18)) $opensips_update_every '' '' 'opensips' +DIMENSION shmem_total_size total absolute 1 1024 +DIMENSION shmem_used_size used absolute 1 1024 +DIMENSION shmem_real_used_size real_used absolute 1 1024 +DIMENSION shmem_max_used_size max_used absolute 1 1024 +DIMENSION shmem_free_size free absolute 1 1024 + +CHART opensips.shmem_fragments '' "OpenSIPS Shared Memory Fragmentation" "fragments" mem '' line $((opensips_priority + 19)) $opensips_update_every '' '' 'opensips' +DIMENSION shmem_fragments fragments absolute 1 1 +EOF + + return 0 +} + +opensips_update() { + # the first argument to this function is the microseconds since last update + # pass this parameter to the BEGIN statement (see below). + + # do all the work to collect / calculate the values + # for each dimension + + # 1. get the counters page from opensips + # 2. sed to remove spaces; replace . with _; remove spaces around =; prepend each line with: local opensips_ + # 3. egrep lines starting with: + # local opensips_client_http_ then one or more of these a-z 0-9 _ then = and one of more of 0-9 + # local opensips_server_all_ then one or more of these a-z 0-9 _ then = and one of more of 0-9 + # 4. then execute this as a script with the eval + # be very careful with eval: + # prepare the script and always grep at the end the lines that are useful, so that + # even if something goes wrong, no other code can be executed + + unset \ + opensips_dialog_active_dialogs \ + opensips_dialog_early_dialogs \ + opensips_usrloc_registered_users \ + opensips_usrloc_location_users \ + opensips_usrloc_location_contacts \ + opensips_usrloc_location_expires \ + opensips_registrar_accepted_regs \ + opensips_registrar_rejected_regs \ + opensips_tm_UAS_transactions \ + opensips_tm_UAC_transactions \ + opensips_core_rcv_requests \ + opensips_core_rcv_replies \ + opensips_core_fwd_requests \ + opensips_core_fwd_replies \ + opensips_core_drop_requests \ + opensips_core_drop_replies \ + opensips_core_err_requests \ + opensips_core_err_replies \ + opensips_core_bad_URIs_rcvd \ + opensips_core_unsupported_methods \ + opensips_core_bad_msg_hdr \ + opensips_tm_received_replies \ + opensips_tm_relayed_replies \ + opensips_tm_local_replies \ + opensips_tm_2xx_transactions \ + opensips_tm_3xx_transactions \ + opensips_tm_4xx_transactions \ + opensips_tm_5xx_transactions \ + opensips_tm_6xx_transactions \ + opensips_tm_inuse_transactions \ + opensips_sl_1xx_replies \ + opensips_sl_2xx_replies \ + opensips_sl_3xx_replies \ + opensips_sl_4xx_replies \ + opensips_sl_5xx_replies \ + opensips_sl_6xx_replies \ + opensips_sl_sent_replies \ + opensips_sl_sent_err_replies \ + opensips_sl_received_ACKs \ + opensips_dialog_processed_dialogs \ + opensips_dialog_expired_dialogs \ + opensips_dialog_failed_dialogs \ + opensips_net_waiting_udp \ + opensips_net_waiting_tcp \ + opensips_uri_positive_checks \ + opensips_uri_negative_checks \ + opensips_siptrace_traced_requests \ + opensips_siptrace_traced_replies \ + opensips_shmem_total_size \ + opensips_shmem_used_size \ + opensips_shmem_real_used_size \ + opensips_shmem_max_used_size \ + opensips_shmem_free_size \ + opensips_shmem_fragments + + opensips_command_failed=0 + eval "local $(opensips_get_stats)" + # shellcheck disable=SC2181 + [ $? -ne 0 ] && return 1 + + [ $opensips_command_failed -eq 1 ] && error "failed to get values, disabling." && return 1 + + # write the result of the work. + cat << VALUESEOF +BEGIN opensips.dialogs_active $1 +SET dialog_active_dialogs = $opensips_dialog_active_dialogs +SET dialog_early_dialogs = $opensips_dialog_early_dialogs +END +BEGIN opensips.users $1 +SET usrloc_registered_users = $opensips_usrloc_registered_users +SET usrloc_location_users = $opensips_usrloc_location_users +SET usrloc_location_contacts = $opensips_usrloc_location_contacts +SET usrloc_location_expires = $opensips_usrloc_location_expires +END +BEGIN opensips.registrar $1 +SET registrar_accepted_regs = $opensips_registrar_accepted_regs +SET registrar_rejected_regs = $opensips_registrar_rejected_regs +END +BEGIN opensips.transactions $1 +SET tm_UAS_transactions = $opensips_tm_UAS_transactions +SET tm_UAC_transactions = $opensips_tm_UAC_transactions +END +BEGIN opensips.core_rcv $1 +SET core_rcv_requests = $opensips_core_rcv_requests +SET core_rcv_replies = $opensips_core_rcv_replies +END +BEGIN opensips.core_fwd $1 +SET core_fwd_requests = $opensips_core_fwd_requests +SET core_fwd_replies = $opensips_core_fwd_replies +END +BEGIN opensips.core_drop $1 +SET core_drop_requests = $opensips_core_drop_requests +SET core_drop_replies = $opensips_core_drop_replies +END +BEGIN opensips.core_err $1 +SET core_err_requests = $opensips_core_err_requests +SET core_err_replies = $opensips_core_err_replies +END +BEGIN opensips.core_bad $1 +SET core_bad_URIs_rcvd = $opensips_core_bad_URIs_rcvd +SET core_unsupported_methods = $opensips_core_unsupported_methods +SET core_bad_msg_hdr = $opensips_core_bad_msg_hdr +END +BEGIN opensips.tm_replies $1 +SET tm_received_replies = $opensips_tm_received_replies +SET tm_relayed_replies = $opensips_tm_relayed_replies +SET tm_local_replies = $opensips_tm_local_replies +END +BEGIN opensips.transactions_status $1 +SET tm_2xx_transactions = $opensips_tm_2xx_transactions +SET tm_3xx_transactions = $opensips_tm_3xx_transactions +SET tm_4xx_transactions = $opensips_tm_4xx_transactions +SET tm_5xx_transactions = $opensips_tm_5xx_transactions +SET tm_6xx_transactions = $opensips_tm_6xx_transactions +END +BEGIN opensips.transactions_inuse $1 +SET tm_inuse_transactions = $opensips_tm_inuse_transactions +END +BEGIN opensips.sl_replies $1 +SET sl_1xx_replies = $opensips_sl_1xx_replies +SET sl_2xx_replies = $opensips_sl_2xx_replies +SET sl_3xx_replies = $opensips_sl_3xx_replies +SET sl_4xx_replies = $opensips_sl_4xx_replies +SET sl_5xx_replies = $opensips_sl_5xx_replies +SET sl_6xx_replies = $opensips_sl_6xx_replies +SET sl_sent_replies = $opensips_sl_sent_replies +SET sl_sent_err_replies = $opensips_sl_sent_err_replies +SET sl_received_ACKs = $opensips_sl_received_ACKs +END +BEGIN opensips.dialogs $1 +SET dialog_processed_dialogs = $opensips_dialog_processed_dialogs +SET dialog_expired_dialogs = $opensips_dialog_expired_dialogs +SET dialog_failed_dialogs = $opensips_dialog_failed_dialogs +END +BEGIN opensips.net_waiting $1 +SET net_waiting_udp = $opensips_net_waiting_udp +SET net_waiting_tcp = $opensips_net_waiting_tcp +END +BEGIN opensips.uri_checks $1 +SET uri_positive_checks = $opensips_uri_positive_checks +SET uri_negative_checks = $opensips_uri_negative_checks +END +BEGIN opensips.traces $1 +SET siptrace_traced_requests = $opensips_siptrace_traced_requests +SET siptrace_traced_replies = $opensips_siptrace_traced_replies +END +BEGIN opensips.shmem $1 +SET shmem_total_size = $opensips_shmem_total_size +SET shmem_used_size = $opensips_shmem_used_size +SET shmem_real_used_size = $opensips_shmem_real_used_size +SET shmem_max_used_size = $opensips_shmem_max_used_size +SET shmem_free_size = $opensips_shmem_free_size +END +BEGIN opensips.shmem_fragments $1 +SET shmem_fragments = $opensips_shmem_fragments +END +VALUESEOF + + return 0 +} diff --git a/src/collectors/charts.d.plugin/opensips/opensips.conf b/src/collectors/charts.d.plugin/opensips/opensips.conf new file mode 100644 index 000000000..e25111dce --- /dev/null +++ b/src/collectors/charts.d.plugin/opensips/opensips.conf @@ -0,0 +1,21 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2018 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +#opensips_opts="fifo get_statistics all" +#opensips_cmd= +#opensips_timeout=2 + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#opensips_update_every=5 + +# the charts priority on the dashboard +#opensips_priority=80000 + +# the number of retries to do in case of failure +# before disabling the module +#opensips_retries=10 diff --git a/src/collectors/charts.d.plugin/sensors/README.md b/src/collectors/charts.d.plugin/sensors/README.md new file mode 120000 index 000000000..7e5a416c4 --- /dev/null +++ b/src/collectors/charts.d.plugin/sensors/README.md @@ -0,0 +1 @@ +integrations/linux_sensors_sysfs.md
\ No newline at end of file diff --git a/src/collectors/charts.d.plugin/sensors/integrations/linux_sensors_sysfs.md b/src/collectors/charts.d.plugin/sensors/integrations/linux_sensors_sysfs.md new file mode 100644 index 000000000..6e802bb7e --- /dev/null +++ b/src/collectors/charts.d.plugin/sensors/integrations/linux_sensors_sysfs.md @@ -0,0 +1,201 @@ +<!--startmeta +custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/collectors/charts.d.plugin/sensors/README.md" +meta_yaml: "https://github.com/netdata/netdata/edit/master/src/collectors/charts.d.plugin/sensors/metadata.yaml" +sidebar_label: "Linux Sensors (sysfs)" +learn_status: "Published" +learn_rel_path: "Collecting Metrics/Hardware Devices and Sensors" +most_popular: False +message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE" +endmeta--> + +# Linux Sensors (sysfs) + + +<img src="https://netdata.cloud/img/microchip.svg" width="150"/> + + +Plugin: charts.d.plugin +Module: sensors + +<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" /> + +## Overview + +Use this collector when `lm-sensors` doesn't work on your device (e.g. for RPi temperatures). +For all other cases use the [Python collector](https://github.com/netdata/netdata/blob/master/src/collectors/python.d.plugin/sensors), which supports multiple jobs, is more efficient and performs calculations on top of the kernel provided values." + + +It will provide charts for all configured system sensors, by reading sensors directly from the kernel. +The values graphed are the raw hardware values of the sensors. + + +This collector is only supported on the following platforms: + +- Linux + +This collector supports collecting metrics from multiple instances of this integration, including remote instances. + + +### Default Behavior + +#### Auto-Detection + +By default, the collector will try to read entries under `/sys/devices` + +#### Limits + +The default configuration for this integration does not impose any limits on data collection. + +#### Performance Impact + +The default configuration for this integration is not expected to impose a significant performance impact on the system. + + +## Metrics + +Metrics grouped by *scope*. + +The scope defines the instance that the metric belongs to. An instance is uniquely identified by a set of labels. + + + +### Per sensor chip + +Metrics related to sensor chips. Each chip provides its own set of the following metrics. + +This scope has no labels. + +Metrics: + +| Metric | Dimensions | Unit | +|:------|:----------|:----| +| sensors.temp | {filename} | Celsius | +| sensors.volt | {filename} | Volts | +| sensors.curr | {filename} | Ampere | +| sensors.power | {filename} | Watt | +| sensors.fans | {filename} | Rotations / Minute | +| sensors.energy | {filename} | Joule | +| sensors.humidity | {filename} | Percent | + + + +## Alerts + +There are no alerts configured by default for this integration. + + +## Setup + +### Prerequisites + +#### Install charts.d plugin + +If [using our official native DEB/RPM packages](https://github.com/netdata/netdata/blob/master/packaging/installer/UPDATE.md#determine-which-installation-method-you-used), make sure `netdata-plugin-chartsd` is installed. + + +#### Enable the sensors collector + +The `sensors` collector is disabled by default. To enable it, use `edit-config` from the Netdata [config directory](https://github.com/netdata/netdata/blob/master/docs/netdata-agent/configuration.md), which is typically at `/etc/netdata`, to edit the `charts.d.conf` file. + +```bash +cd /etc/netdata # Replace this path with your Netdata config directory, if different +sudo ./edit-config charts.d.conf +``` + +Change the value of the `sensors` setting to `force` and uncomment the line. Save the file and restart the Netdata Agent with `sudo systemctl restart netdata`, or the [appropriate method](https://github.com/netdata/netdata/blob/master/packaging/installer/README.md#maintaining-a-netdata-agent-installation) for your system. + + + +### Configuration + +#### File + +The configuration file name for this integration is `charts.d/sensors.conf`. + + +You can edit the configuration file using the `edit-config` script from the +Netdata [config directory](https://github.com/netdata/netdata/blob/master/docs/netdata-agent/configuration.md#the-netdata-config-directory). + +```bash +cd /etc/netdata 2>/dev/null || cd /opt/netdata/etc/netdata +sudo ./edit-config charts.d/sensors.conf +``` +#### Options + +The config file is sourced by the charts.d plugin. It's a standard bash file. + +The following collapsed table contains all the options that can be configured for the sensors collector. + + +<details><summary>Config options</summary> + +| Name | Description | Default | Required | +|:----|:-----------|:-------|:--------:| +| sensors_sys_dir | The directory the kernel exposes sensor data. | /sys/devices | no | +| sensors_sys_depth | How deep in the tree to check for sensor data. | 10 | no | +| sensors_source_update | If set to 1, the script will overwrite internal script functions with code generated ones. | 1 | no | +| sensors_update_every | The data collection frequency. If unset, will inherit the netdata update frequency. | 1 | no | +| sensors_priority | The charts priority on the dashboard. | 90000 | no | +| sensors_retries | The number of retries to do in case of failure before disabling the collector. | 10 | no | + +</details> + +#### Examples + +##### Set sensors path depth + +Set a different sensors path depth + +```yaml +# the directory the kernel keeps sensor data +#sensors_sys_dir="/sys/devices" + +# how deep in the tree to check for sensor data +sensors_sys_depth=5 + +# if set to 1, the script will overwrite internal +# script functions with code generated ones +# leave to 1, is faster +#sensors_source_update=1 + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#sensors_update_every= + +# the charts priority on the dashboard +#sensors_priority=90000 + +# the number of retries to do in case of failure +# before disabling the module +#sensors_retries=10 + +``` + + +## Troubleshooting + +### Debug Mode + +To troubleshoot issues with the `sensors` collector, run the `charts.d.plugin` with the debug option enabled. The output +should give you clues as to why the collector isn't working. + +- Navigate to the `plugins.d` directory, usually at `/usr/libexec/netdata/plugins.d/`. If that's not the case on + your system, open `netdata.conf` and look for the `plugins` setting under `[directories]`. + + ```bash + cd /usr/libexec/netdata/plugins.d/ + ``` + +- Switch to the `netdata` user. + + ```bash + sudo -u netdata -s + ``` + +- Run the `charts.d.plugin` to debug the collector: + + ```bash + ./charts.d.plugin debug 1 sensors + ``` + + diff --git a/src/collectors/charts.d.plugin/sensors/metadata.yaml b/src/collectors/charts.d.plugin/sensors/metadata.yaml new file mode 100644 index 000000000..ef32ee723 --- /dev/null +++ b/src/collectors/charts.d.plugin/sensors/metadata.yaml @@ -0,0 +1,182 @@ +plugin_name: charts.d.plugin +modules: + - meta: + plugin_name: charts.d.plugin + module_name: sensors + monitored_instance: + name: Linux Sensors (sysfs) + link: "https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface" + categories: + - data-collection.hardware-devices-and-sensors + icon_filename: "microchip.svg" + related_resources: + integrations: + list: [] + info_provided_to_referring_integrations: + description: "" + keywords: + - sensors + - sysfs + - hwmon + - rpi + - raspberry pi + most_popular: false + overview: + data_collection: + metrics_description: | + Use this collector when `lm-sensors` doesn't work on your device (e.g. for RPi temperatures). + For all other cases use the [Python collector](https://github.com/netdata/netdata/blob/master/src/collectors/python.d.plugin/sensors), which supports multiple jobs, is more efficient and performs calculations on top of the kernel provided values." + method_description: | + It will provide charts for all configured system sensors, by reading sensors directly from the kernel. + The values graphed are the raw hardware values of the sensors. + supported_platforms: + include: [Linux] + exclude: [] + multi_instance: true + additional_permissions: + description: "" + default_behavior: + auto_detection: + description: "By default, the collector will try to read entries under `/sys/devices`" + limits: + description: "" + performance_impact: + description: "" + setup: + prerequisites: + list: + - title: "Install charts.d plugin" + description: | + If [using our official native DEB/RPM packages](https://github.com/netdata/netdata/blob/master/packaging/installer/UPDATE.md#determine-which-installation-method-you-used), make sure `netdata-plugin-chartsd` is installed. + - title: "Enable the sensors collector" + description: | + The `sensors` collector is disabled by default. To enable it, use `edit-config` from the Netdata [config directory](https://github.com/netdata/netdata/blob/master/docs/netdata-agent/configuration.md), which is typically at `/etc/netdata`, to edit the `charts.d.conf` file. + + ```bash + cd /etc/netdata # Replace this path with your Netdata config directory, if different + sudo ./edit-config charts.d.conf + ``` + + Change the value of the `sensors` setting to `force` and uncomment the line. Save the file and restart the Netdata Agent with `sudo systemctl restart netdata`, or the [appropriate method](https://github.com/netdata/netdata/blob/master/packaging/installer/README.md#maintaining-a-netdata-agent-installation) for your system. + configuration: + file: + name: charts.d/sensors.conf + options: + description: | + The config file is sourced by the charts.d plugin. It's a standard bash file. + + The following collapsed table contains all the options that can be configured for the sensors collector. + folding: + title: "Config options" + enabled: true + list: + - name: sensors_sys_dir + description: The directory the kernel exposes sensor data. + default_value: "/sys/devices" + required: false + - name: sensors_sys_depth + description: How deep in the tree to check for sensor data. + default_value: 10 + required: false + - name: sensors_source_update + description: If set to 1, the script will overwrite internal script functions with code generated ones. + default_value: 1 + required: false + - name: sensors_update_every + description: The data collection frequency. If unset, will inherit the netdata update frequency. + default_value: 1 + required: false + - name: sensors_priority + description: The charts priority on the dashboard. + default_value: 90000 + required: false + - name: sensors_retries + description: The number of retries to do in case of failure before disabling the collector. + default_value: 10 + required: false + examples: + folding: + enabled: false + title: "Config" + list: + - name: Set sensors path depth + description: Set a different sensors path depth + config: | + # the directory the kernel keeps sensor data + #sensors_sys_dir="/sys/devices" + + # how deep in the tree to check for sensor data + sensors_sys_depth=5 + + # if set to 1, the script will overwrite internal + # script functions with code generated ones + # leave to 1, is faster + #sensors_source_update=1 + + # the data collection frequency + # if unset, will inherit the netdata update frequency + #sensors_update_every= + + # the charts priority on the dashboard + #sensors_priority=90000 + + # the number of retries to do in case of failure + # before disabling the module + #sensors_retries=10 + troubleshooting: + problems: + list: [] + alerts: [] + metrics: + folding: + title: Metrics + enabled: false + description: "" + availability: [] + scopes: + - name: sensor chip + description: "Metrics related to sensor chips. Each chip provides its own set of the following metrics." + labels: [] + metrics: + - name: sensors.temp + description: Temperature + unit: "Celsius" + chart_type: line + dimensions: + - name: "{filename}" + - name: sensors.volt + description: Voltage + unit: "Volts" + chart_type: line + dimensions: + - name: "{filename}" + - name: sensors.curr + description: Current + unit: "Ampere" + chart_type: line + dimensions: + - name: "{filename}" + - name: sensors.power + description: Power + unit: "Watt" + chart_type: line + dimensions: + - name: "{filename}" + - name: sensors.fans + description: Fans Speed + unit: "Rotations / Minute" + chart_type: line + dimensions: + - name: "{filename}" + - name: sensors.energy + description: Energy + unit: "Joule" + chart_type: area + dimensions: + - name: "{filename}" + - name: sensors.humidity + description: Humidity + unit: "Percent" + chart_type: line + dimensions: + - name: "{filename}" diff --git a/src/collectors/charts.d.plugin/sensors/sensors.chart.sh b/src/collectors/charts.d.plugin/sensors/sensors.chart.sh new file mode 100644 index 000000000..9576e2ab2 --- /dev/null +++ b/src/collectors/charts.d.plugin/sensors/sensors.chart.sh @@ -0,0 +1,250 @@ +# shellcheck shell=bash +# no need for shebang - this file is loaded from charts.d.plugin +# SPDX-License-Identifier: GPL-3.0-or-later + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +# + +# sensors docs +# https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface + +# if this chart is called X.chart.sh, then all functions and global variables +# must start with X_ + +# the directory the kernel keeps sensor data +sensors_sys_dir="${NETDATA_HOST_PREFIX}/sys/devices" + +# how deep in the tree to check for sensor data +sensors_sys_depth=10 + +# if set to 1, the script will overwrite internal +# script functions with code generated ones +# leave to 1, is faster +sensors_source_update=1 + +# how frequently to collect sensor data +# the default is to collect it at every iteration of charts.d +sensors_update_every= + +sensors_priority=90000 + +declare -A sensors_excluded=() + +sensors_find_all_files() { + find "$1" -maxdepth $sensors_sys_depth -name \*_input -o -name temp 2>/dev/null +} + +sensors_find_all_dirs() { + # shellcheck disable=SC2162 + sensors_find_all_files "$1" | while read; do + dirname "$REPLY" + done | sort -u +} + +# _check is called once, to find out if this chart should be enabled or not +sensors_check() { + + # this should return: + # - 0 to enable the chart + # - 1 to disable the chart + + [ -z "$(sensors_find_all_files "$sensors_sys_dir")" ] && error "no sensors found in '$sensors_sys_dir'." && return 1 + return 0 +} + +sensors_check_files() { + # we only need sensors that report a non-zero value + # also remove not needed sensors + + local f v excluded + for f in "$@"; do + [ ! -f "$f" ] && continue + for ex in "${sensors_excluded[@]}"; do + [[ $f =~ .*$ex$ ]] && excluded='1' && break + done + + [ "$excluded" != "1" ] && v="$(cat "$f")" || v=0 + v=$((v + 1 - 1)) + [ $v -ne 0 ] && echo "$f" && continue + excluded= + + error "$f gives zero values" + done +} + +sensors_check_temp_type() { + # valid temp types are 1 to 6 + # disabled sensors have the value 0 + + local f t v + for f in "$@"; do + # shellcheck disable=SC2001 + t=$(echo "$f" | sed "s|_input$|_type|g") + [ "$f" = "$t" ] && echo "$f" && continue + [ ! -f "$t" ] && echo "$f" && continue + + v="$(cat "$t")" + v=$((v + 1 - 1)) + [ $v -ne 0 ] && echo "$f" && continue + + error "$f is disabled" + done +} + +# _create is called once, to create the charts +sensors_create() { + local path dir name x file lfile labelname device subsystem id type mode files multiplier divisor + + # we create a script with the source of the + # sensors_update() function + # - the highest speed we can achieve - + [ $sensors_source_update -eq 1 ] && echo >"$TMP_DIR/sensors.sh" "sensors_update() {" + + for path in $(sensors_find_all_dirs "$sensors_sys_dir" | sort -u); do + dir=$(basename "$path") + device= + subsystem= + id= + type= + name= + + [ -h "$path/device" ] && device=$(readlink -f "$path/device") + [ ! -z "$device" ] && device=$(basename "$device") + [ -z "$device" ] && device="$dir" + + [ -h "$path/subsystem" ] && subsystem=$(readlink -f "$path/subsystem") + [ ! -z "$subsystem" ] && subsystem=$(basename "$subsystem") + [ -z "$subsystem" ] && subsystem="$dir" + + [ -f "$path/name" ] && name=$(cat "$path/name") + [ -z "$name" ] && name="$dir" + + [ -f "$path/type" ] && type=$(cat "$path/type") + [ -z "$type" ] && type="$dir" + + id="$(fixid "$device.$subsystem.$dir")" + + debug "path='$path', dir='$dir', device='$device', subsystem='$subsystem', id='$id', name='$name'" + + for mode in temperature voltage fans power current energy humidity; do + files= + multiplier=1 + divisor=1 + algorithm="absolute" + + case $mode in + temperature) + files="$( + ls "$path"/temp*_input 2>/dev/null + ls "$path/temp" 2>/dev/null + )" + files="$(sensors_check_files "$files")" + files="$(sensors_check_temp_type "$files")" + [ -z "$files" ] && continue + echo "CHART 'sensors.temp_${id}_${name}' '' 'Temperature' 'Celsius' 'temperature' 'sensors.temp' line $((sensors_priority + 1)) $sensors_update_every '' '' 'sensors'" + echo >>"$TMP_DIR/sensors.sh" "echo \"BEGIN 'sensors.temp_${id}_${name}' \$1\"" + divisor=1000 + ;; + + voltage) + files="$(ls "$path"/in*_input 2>/dev/null)" + files="$(sensors_check_files "$files")" + [ -z "$files" ] && continue + echo "CHART 'sensors.volt_${id}_${name}' '' 'Voltage' 'Volts' 'voltage' 'sensors.volt' line $((sensors_priority + 2)) $sensors_update_every '' '' 'sensors'" + echo >>"$TMP_DIR/sensors.sh" "echo \"BEGIN 'sensors.volt_${id}_${name}' \$1\"" + divisor=1000 + ;; + + current) + files="$(ls "$path"/curr*_input 2>/dev/null)" + files="$(sensors_check_files "$files")" + [ -z "$files" ] && continue + echo "CHART 'sensors.curr_${id}_${name}' '' 'Current' 'Ampere' 'current' 'sensors.curr' line $((sensors_priority + 3)) $sensors_update_every '' '' 'sensors'" + echo >>"$TMP_DIR/sensors.sh" "echo \"BEGIN 'sensors.curr_${id}_${name}' \$1\"" + divisor=1000 + ;; + + power) + files="$(ls "$path"/power*_input 2>/dev/null)" + files="$(sensors_check_files "$files")" + [ -z "$files" ] && continue + echo "CHART 'sensors.power_${id}_${name}' '' 'Power' 'Watt' 'power' 'sensors.power' line $((sensors_priority + 4)) $sensors_update_every '' '' 'sensors'" + echo >>"$TMP_DIR/sensors.sh" "echo \"BEGIN 'sensors.power_${id}_${name}' \$1\"" + divisor=1000000 + ;; + + fans) + files="$(ls "$path"/fan*_input 2>/dev/null)" + files="$(sensors_check_files "$files")" + [ -z "$files" ] && continue + echo "CHART 'sensors.fan_${id}_${name}' '' 'Fans Speed' 'Rotations / Minute' 'fans' 'sensors.fans' line $((sensors_priority + 5)) $sensors_update_every '' '' 'sensors'" + echo >>"$TMP_DIR/sensors.sh" "echo \"BEGIN 'sensors.fan_${id}_${name}' \$1\"" + ;; + + energy) + files="$(ls "$path"/energy*_input 2>/dev/null)" + files="$(sensors_check_files "$files")" + [ -z "$files" ] && continue + echo "CHART 'sensors.energy_${id}_${name}' '' 'Energy' 'Joule' 'energy' 'sensors.energy' area $((sensors_priority + 6)) $sensors_update_every '' '' 'sensors'" + echo >>"$TMP_DIR/sensors.sh" "echo \"BEGIN 'sensors.energy_${id}_${name}' \$1\"" + algorithm="incremental" + divisor=1000000 + ;; + + humidity) + files="$(ls "$path"/humidity*_input 2>/dev/null)" + files="$(sensors_check_files "$files")" + [ -z "$files" ] && continue + echo "CHART 'sensors.humidity_${id}_${name}' '' 'Humidity' 'Percent' 'humidity' 'sensors.humidity' line $((sensors_priority + 7)) $sensors_update_every '' '' 'sensors'" + echo >>"$TMP_DIR/sensors.sh" "echo \"BEGIN 'sensors.humidity_${id}_${name}' \$1\"" + divisor=1000 + ;; + + *) + continue + ;; + esac + + for x in $files; do + file="$x" + fid="$(fixid "$file")" + lfile="$(basename "$file" | sed "s|_input$|_label|g")" + labelname="$(basename "$file" | sed "s|_input$||g")" + + if [ ! "$path/$lfile" = "$file" ] && [ -f "$path/$lfile" ]; then + labelname="$(cat "$path/$lfile")" + fi + + echo "DIMENSION $fid '$labelname' $algorithm $multiplier $divisor" + echo >>"$TMP_DIR/sensors.sh" "echo \"SET $fid = \"\$(< $file )" + done + + echo >>"$TMP_DIR/sensors.sh" "echo END" + done + done + + [ $sensors_source_update -eq 1 ] && echo >>"$TMP_DIR/sensors.sh" "}" + + # ok, load the function sensors_update() we created + # shellcheck source=/dev/null + [ $sensors_source_update -eq 1 ] && . "$TMP_DIR/sensors.sh" + + return 0 +} + +# _update is called continuously, to collect the values +sensors_update() { + # the first argument to this function is the microseconds since last update + # pass this parameter to the BEGIN statement (see below). + + # do all the work to collect / calculate the values + # for each dimension + # remember: KEEP IT SIMPLE AND SHORT + + # shellcheck source=/dev/null + [ $sensors_source_update -eq 0 ] && . "$TMP_DIR/sensors.sh" "$1" + + return 0 +} diff --git a/src/collectors/charts.d.plugin/sensors/sensors.conf b/src/collectors/charts.d.plugin/sensors/sensors.conf new file mode 100644 index 000000000..bcb28807d --- /dev/null +++ b/src/collectors/charts.d.plugin/sensors/sensors.conf @@ -0,0 +1,32 @@ +# no need for shebang - this file is loaded from charts.d.plugin + +# netdata +# real-time performance and health monitoring, done right! +# (C) 2018 Costa Tsaousis <costa@tsaousis.gr> +# GPL v3+ + +# THIS PLUGIN IS DEPRECATED +# USE THE PYTHON.D ONE + +# the directory the kernel keeps sensor data +#sensors_sys_dir="/sys/devices" + +# how deep in the tree to check for sensor data +#sensors_sys_depth=10 + +# if set to 1, the script will overwrite internal +# script functions with code generated ones +# leave to 1, is faster +#sensors_source_update=1 + +# the data collection frequency +# if unset, will inherit the netdata update frequency +#sensors_update_every= + +# the charts priority on the dashboard +#sensors_priority=90000 + +# the number of retries to do in case of failure +# before disabling the module +#sensors_retries=10 + |