profile/benchmark-registry
*.pyc
+
+diagrams/*.png
+diagrams/*.svg
+diagrams/*.atxt
+diagrams/plantuml.jar
+
+netdata.cppcheck
src/daemon.h
src/dictionary.c
src/dictionary.h
+ src/eval.c
+ src/eval.h
src/global_statistics.c
src/global_statistics.h
+ src/health.c
+ src/health.h
src/log.c
src/log.h
src/main.c
src/proc_net_rpc_nfsd.c
src/proc_net_snmp6.c
src/proc_net_snmp.c
+ src/proc_net_softnet_stat.c
src/proc_net_stat_conntrack.c
src/proc_net_stat_synproxy.c
- src/proc_net_softnet_stat.c
src/proc_self_mountinfo.c
src/proc_self_mountinfo.h
src/proc_softirqs.c
src/rrd.h
src/storage_number.c
src/storage_number.h
- src/sys_kernel_mm_ksm.c
src/sys_fs_cgroup.c
+ src/sys_kernel_mm_ksm.c
src/unit_test.c
src/unit_test.h
src/url.c
src/web_client.h
src/web_server.c
src/web_server.h
- config.h src/health.h src/health.c src/eval.h src/eval.c)
+)
set(APPS_PLUGIN_SOURCE_FILES
src/appconfig.c
[MIT License](http://getbootstrap.com/getting-started/#license-faqs)
-- [NanoScroller](https://jamesflorentino.github.io/nanoScrollerJS/)
-
- Copyright 2012, James Florentino
- [MIT License](https://github.com/jamesflorentino/nanoScrollerJS/blob/master/LICENSE)
-
-
- [Bootstrap Toggle](http://www.bootstraptoggle.com/)
Copyright (c) 2011-2014 Min Hur, The New York Times Company
[MIT License](https://github.com/minhur/bootstrap-toggle/blob/master/LICENSE)
-- [AVL C Library](http://freecode.com/projects/avl)
+- [bootstrap-table](http://bootstrap-table.wenzhixin.net.cn/)
+
+ Copyright (c) 2012-2016 Zhixin Wen <wenzhixin2010@gmail.com>
+ [MIT License](https://github.com/wenzhixin/bootstrap-table/blob/master/LICENSE)
+
+
+- [tableExport.jquery.plugin](https://github.com/hhurz/tableExport.jquery.plugin)
+
+ Copyright (c) 2015,2016 hhurz
+ [MIT License](http://rawgit.com/hhurz/tableExport.jquery.plugin/master/tableExport.js)
+
+
+- [NanoScroller](https://jamesflorentino.github.io/nanoScrollerJS/)
- Copyright 2000, Daniel Nagy, Budapest University of Technology and Economics
- Released under GNU General Public License (GPL) version 2
+ Copyright 2012, James Florentino
+ [MIT License](https://github.com/jamesflorentino/nanoScrollerJS/blob/master/LICENSE)
- [FontAwesome](https://fortawesome.github.io/Font-Awesome/)
Copyright 2006, Kirill Simonov
[MIT License](http://pyyaml.org)
-- [bootstrap-table](http://bootstrap-table.wenzhixin.net.cn/)
-
- Copyright (c) 2012-2016 Zhixin Wen <wenzhixin2010@gmail.com>
- [MIT License](https://github.com/wenzhixin/bootstrap-table/blob/master/LICENSE)
-
-- [tableExport.jquery.plugin](https://github.com/hhurz/tableExport.jquery.plugin)
-
- Copyright (c) 2015,2016 hhurz
- [MIT License](http://rawgit.com/hhurz/tableExport.jquery.plugin/master/tableExport.js)
-
-
$(NULL)
dist_noinst_DATA= \
+ diagrams/config.puml \
+ diagrams/registry.puml \
configs.signatures \
Dockerfile \
netdata.spec \
# until integrated within build
# should be proper init.d/openrc/systemd usable
dist_noinst_SCRIPTS= \
- ansible/netdata.yml \
+ diagrams/build.sh \
coverity-scan.sh \
docker-build.sh \
netdata-installer.sh \
> *New to netdata? Here is a live demo: [http://my-netdata.io](http://my-netdata.io)*
-**netdata** is an application for **real-time performance and health monitoring**. It provides **unparalleled insights, in real-time**, of everything happening on the system it runs (including key applications such as web and database servers), using **modern interactive web dashboards**.
+**netdata** is a system for **distributed real-time performance and health monitoring**.
+It provides **unparalleled insights, in real-time**, of everything happening on the
+system it runs (including applications such as web, or database servers), using
+**modern interactive web dashboards**.
-_netdata is **very fast** and **super efficient**, designed to permanently run on all systems (**physical** & **virtual** servers, **containers**, **IoT** devices), without disrupting their core function._
+_netdata is **fast** and **efficient**, designed to permanently run on all systems
+(**physical** & **virtual** servers, **containers**, **IoT** devices), without
+disrupting their core function._
---
`Oct 4th, 2016` - **[netdata v1.4.0 released!](https://github.com/firehol/netdata/releases)**
- - the **fastest** netdata ever (with a better look too)!
- - improved **IoT** and **containers** support!
- - **alarms** improved in almost every way!
- - new plugins: softnet netdev, extended TCP metrics, UDPLite, NFS v2, v3 client (server was there already), NFS v4 server & client, APCUPSd, RetroShare
+ - the **fastest** netdata ever (with a better look too)
+ - improved **IoT** and **containers** support
+ - **alarms** improved in almost every way
+ - new plugins: softnet netdev, extended TCP metrics, UDPLite, NFS v2, v3 client (server was there already), NFS v4 server & client, APCUPSd, RetroShare
- improved plugins: mysql, cgroups, hddtemp, sensors, phpfm, tc (QoS)
---
<img src="https://cloud.githubusercontent.com/assets/2662304/19168687/f6a567be-8c19-11e6-8561-ce8d589e8346.gif"/>
</p>
- - **Stunning interactive bootstrap dashboards**<br/>2 themes are provided: dark, light
- - **Blazingly fast visualization**<br/>responds to all queries in less than 0.5 ms per metric, even on low-end hardware
- - **Highly efficient data collection**<br/>collects thousands of metrics per server per second, with just 1% CPU utilization of a single core on modern hardware
- - **Sophisticated alarming**<br/>supports dynamic thresholds, hysteresis, templating, multiple notification methods
- - **Extensible**<br/>you can monitor anything you can get a metric for, using its Plugin API (anything can be a netdata plugin, BASH, python, perl, node.js, java, Go, etc)
- - **Zero configuration**<br/>auto-detects everything, out of the box it can collect up to 5000 metrics per server
- - **Zero dependencies**<br/>it is even its own web server, for its static web files and its web API
- - **Zero maintenance**<br/>you just run it, it does the rest
- - **Embeddable**<br/>it can run anywhere a Linux kernel runs (even IoT) and its charts can be embedded on your web pages too
- - **Custom dashboards**<br/>that can be built using simple HTML (no javascript necessary)
- - **scales to infinity**<br/>requiring minimal central resources!
+ - **Stunning interactive bootstrap dashboards**<br/>
+ mouse and touch friendly, in 2 themes: dark, light
+
+ - **Blazingly fast**<br/>
+ responds to all queries in less than 0.5 ms per metric,
+ even on low-end hardware (such as a raspberry pi 1)
+
+ - **Highly efficient data collection**<br/>
+ collects thousands of metrics per server per second,
+ with just 1% CPU utilization of a single core, a few MB or RAM and no disk I/O at all
+
+ - **Sophisticated alarming**<br/>
+ supports dynamic thresholds, hysteresis, alarm templates,
+ multiple role-based notification methods (such as slack.com, pushover.net, telegram.org, email)
+
+ - **Extensible**<br/>
+ you can monitor anything you can get a metric for,
+ using its Plugin API (anything can be a netdata plugin,
+ BASH, python, perl, node.js, java, Go, ruby, etc)
+
+ - **Embeddable**<br/>
+ it can run anywhere a Linux kernel runs (even IoT)
+ and its charts can be embedded on your web pages too
+
+ - **Zero configuration**<br/>
+ auto-detects everything, it can collect up to 5000 metrics
+ per server out of the box
+
+ - **Zero dependencies**<br/>
+ it is even its own web server, for its static web files and its web API
+
+ - **Zero maintenance**<br/>
+ you just run it, it does the rest
+
+ - **Custom dashboards**<br/>
+ that can be built using simple HTML (no javascript necessary)
+
+ - **scales to infinity**<br/>
+ requiring minimal central resources
![netdata](https://cloud.githubusercontent.com/assets/2662304/14092712/93b039ea-f551-11e5-822c-beadbf2b2a2e.gif)
## What does it monitor?
-This is what it currently monitors (most with zero configuration):
+netdata monitors several thousands of metrics per device.
+All these metrics are collected and visualized in real-time.
-- **CPU usage, interrupts, softirqs and frequency** (total and per core)
+> _Almost all metrics are auto-detected, without any configuration._
-- **RAM, swap and kernel memory usage** (including KSM and kernel memory deduper)
+This is a list of what it currently monitors:
-- **Disks** (per disk: I/O, operations, backlog, utilization, space, etc)
+- **CPU**<br/>
+ usage, interrupts, softirqs, frequency, total and per core
+
+- **Memory**<br/>
+ RAM, swap and kernel memory usage, including KSM the kernel memory deduper
+
+- **Disks**<br/>
+ per disk: I/O, operations, backlog, utilization, space
![sda](https://cloud.githubusercontent.com/assets/2662304/14093195/c882bbf4-f554-11e5-8863-1788d643d2c0.gif)
-- **Network interfaces** (per interface: bandwidth, packets, errors, drops, etc)
+- **Network interfaces**<br/>
+ per interface: bandwidth, packets, errors, drops
![dsl0](https://cloud.githubusercontent.com/assets/2662304/14093128/4d566494-f554-11e5-8ee4-5392e0ac51f0.gif)
-- **IPv4 networking** (bandwidth, packets, errors, fragments, tcp: connections, packets, errors, handshake, udp: packets, errors, broadcast: bandwidth, packets, multicast: bandwidth, packets)
+- **IPv4 networking**<br/>
+ bandwidth, packets, errors, fragments,
+ tcp: connections, packets, errors, handshake,
+ udp: packets, errors,
+ broadcast: bandwidth, packets,
+ multicast: bandwidth, packets
-- **IPv6 networking** (bandwidth, packets, errors, fragments, ECT, udp: packets, errors, udplite: packets, errors, broadcast: bandwidth, multicast: bandwidth, packets, icmp: messages, errors, echos, router, neighbor, MLDv2, group membership, break down by type)
+- **IPv6 networking**<br/>
+ bandwidth, packets, errors, fragments, ECT,
+ udp: packets, errors,
+ udplite: packets, errors,
+ broadcast: bandwidth,
+ multicast: bandwidth, packets,
+ icmp: messages, errors, echos, router, neighbor, MLDv2, group membership,
+ break down by type
-- **netfilter / iptables Linux firewall** (connections, connection tracker events, errors, etc)
+- **netfilter / iptables Linux firewall**<br/>
+ connections, connection tracker events, errors
-- **Linux DDoS protection** (SYNPROXY metrics)
+- **Linux DDoS protection**<br/>
+ SYNPROXY metrics
-- **Processes** (running, blocked, forks, active, etc)
+- **Processes**<br/>
+ running, blocked, forks, active
-- **Entropy** (random numbers pool, using in cryptography)
+- **Entropy**<br/>
+ random numbers pool, using in cryptography
-- **NFS file servers and clients**, v2, v3, v4 (I/O, cache, read ahead, RPC calls)
+- **NFS file servers and clients**<br/>
+ NFS v2, v3, v4: I/O, cache, read ahead, RPC calls
-- **Network QoS** (yes, the only tool that visualizes network `tc` classes in realtime)
+- **Network QoS**<br/>
+ the only tool that visualizes network `tc` classes in realtime
![qos-tc-classes](https://cloud.githubusercontent.com/assets/2662304/14093004/68966020-f553-11e5-98fe-ffee2086fafd.gif)
-- **Linux Control Groups** (containers), systemd, lxc, docker, etc
+- **Linux Control Groups**<br/>
+ containers: systemd, lxc, docker
-- **Applications**, by grouping the process tree (CPU, memory, disk reads, disk writes, swap, threads, pipes, sockets, etc)
+- **Applications**<br/>
+ by grouping the process tree and reporting CPU, memory, disk reads,
+ disk writes, swap, threads, pipes, sockets - per group
![apps](https://cloud.githubusercontent.com/assets/2662304/14093565/67c4002c-f557-11e5-86bd-0154f5135def.gif)
-- **Users and User Groups resource usage**, by summarizing the process tree per user and group (CPU, memory, disk reads, disk writes, swap, threads, pipes, sockets, etc)
+- **Users and User Groups resource usage**<br/>
+ by summarizing the process tree per user and group,
+ reporting: CPU, memory, disk reads, disk writes, swap, threads, pipes, sockets
-- **Apache web servers** mod-status (v2.2, v2.4) and cache log statistics (multiple servers - compatible with lighttpd too)
+- **Apache and lighttpd web servers**<br/>
+ `mod-status` (v2.2, v2.4) and cache log statistics, for multiple servers
-- **Nginx web servers** stub-status (multiple servers)
+- **Nginx web servers**<br/>
+ `stub-status`, for multiple servers
-- **mySQL databases** (multiple servers, each showing: bandwidth, queries/s, handlers, locks, issues, tmp operations, connections, binlog metrics, threads, innodb metrics, etc)
+- **Tomcat**<br/>
+ accesses, threads, free memory, volume
-- **Redis databases** (multiple servers, each showing: operations, hit rate, memory, keys, clients, slaves)
+- **mySQL databases**<br/>
+ multiple servers, each showing: bandwidth, queries/s, handlers, locks, issues,
+ tmp operations, connections, binlog metrics, threads, innodb metrics, and more
-- **memcached databases** (multiple servers, each showing: bandwidth, connections, items, etc)
+- **Redis databases**<br/>
+ multiple servers, each showing: operations, hit rate, memory, keys, clients, slaves
-- **ISC Bind name servers** (multiple servers, each showing: clients, requests, queries, updates, failures and several per view metrics)
+- **memcached databases**<br/>
+ multiple servers, each showing: bandwidth, connections, items
-- **Postfix email servers** message queue (entries, size)
+- **ISC Bind name servers**<br/>
+ multiple servers, each showing: clients, requests, queries, updates, failures and several per view metrics
-- **exim email servers** message queue (emails queued)
+- **Postfix email servers**<br/>
+ message queue (entries, size)
-- **IPFS** (Bandwidth, Peers)
+- **exim email servers**<br/>
+ message queue (emails queued)
-- **Squid proxy servers** (multiple servers, each showing: clients bandwidth and requests, servers bandwidth and requests)
+- **IPFS**<br/>
+ bandwidth, peers
-- **Hardware sensors** (temperature, voltage, fans, power, humidity, etc)
+- **Squid proxy servers**<br/>
+ multiple servers, each showing: clients bandwidth and requests, servers bandwidth and requests
-- **NUT and APC UPSes** (load, charge, battery voltage, temperature, utility metrics, output metrics)
+- **Hardware sensors**<br/>
+ temperature, voltage, fans, power, humidity
-- **Tomcat** (accesses, threads, free memory, volume)
+- **NUT and APC UPSes**<br/>
+ load, charge, battery voltage, temperature, utility metrics, output metrics
-- **PHP-FPM** (multiple instances, each reporting connections, requests, performance)
+- **PHP-FPM**<br/>
+ multiple instances, each reporting connections, requests, performance
-- **hddtemp** (disk temperatures)
+- **hddtemp**<br/>
+ disk temperatures
-- **SNMP devices** can be monitored too (although you will need to configure these)
+- **SNMP devices**<br/>
+ can be monitored too (although you will need to configure these)
And you can extend it, by writing plugins that collect data from any source, using any computer language.
+++ /dev/null
----
-- name: "Install pre-requisites"
- apt:
- name: "{{ item }}"
- state: present
- with_items:
- - autoconf
- - autoconf-archive
- - autogen
- - automake
- - gcc
- - git
- - libmnl-dev
- - make
- - pkg-config
- - uuid-dev
- - zlib1g-dev
- when: ansible_os_family == "Debian"
-
-- name: "Install pre-requisites"
- yum:
- name: "{{ item }}"
- state: present
- with_items:
- - autoconf
- - autoconf-archive
- - autogen
- - automake
- - curl
- - gcc
- - git
- - jq
- - libmnl-devel
- - libuuid-devel
- - make
- - pkgconfig
- - zlib-devel
- when: ansible_os_family == "RedHat"
-
-- name: "Clone repo"
- git:
- clone: yes
- repo: https://github.com/firehol/netdata.git
- dest: /tmp/netdata
-
-- name: "Installation"
- shell: cd /tmp/netdata/ && ./netdata-installer.sh --dont-wait --libs-are-really-here
-
-- name: "Clean /tmp"
- file:
- path: /tmp/netdata
- state: absent
-
-- name: "KillAll"
- shell: killall netdata
-
-- name: "Daemon config"
- systemd:
- daemon_reload: yes
- name: netdata
- enabled: yes
- state: started
# no need for shebang - this file is loaded from charts.d.plugin
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+# GPL v3+
+#
+
# _update_every is a special variable - it holds the number of seconds
# between the calls of the _update() function
ap_update_every=
declare -A ap_devs=()
-export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/sbin"
-
# _check is called once, to find out if this chart should be enabled or not
ap_check() {
require_cmd iw || return 1
- local ev=$(iw dev | awk '
+ local ev=$(run iw dev | awk '
BEGIN {
i = "";
ssid = "";
# - 1 to disable the chart
[ ${#ap_devs[@]} -gt 0 ] && return 0
+ error "no devices found in AP mode, with 'iw dev'"
return 1
}
# no need for shebang - this file is loaded from charts.d.plugin
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+# GPL v3+
+#
+
# the URL to download apache status info
apache_url="http://127.0.0.1:80/server-status?auto"
apache_curl_opts=
# we will not check of the Conns*
# keys, since these are apache 2.4 specific
- [ -z "${apache_key_accesses}" ] && echo >&2 "apache: missing 'Total Accesses' from apache server: ${*}" && return 1
- [ -z "${apache_key_kbytes}" ] && echo >&2 "apache: missing 'Total kBytes' from apache server: ${*}" && return 1
- [ -z "${apache_key_reqpersec}" ] && echo >&2 "apache: missing 'ReqPerSec' from apache server: ${*}" && return 1
- [ -z "${apache_key_bytespersec}" ] && echo >&2 "apache: missing 'BytesPerSec' from apache server: ${*}" && return 1
- [ -z "${apache_key_bytesperreq}" ] && echo >&2 "apache: missing 'BytesPerReq' from apache server: ${*}" && return 1
- [ -z "${apache_key_busyworkers}" ] && echo >&2 "apache: missing 'BusyWorkers' from apache server: ${*}" && return 1
- [ -z "${apache_key_idleworkers}" ] && echo >&2 "apache: missing 'IdleWorkers' from apache server: ${*}" && return 1
- [ -z "${apache_key_scoreboard}" ] && echo >&2 "apache: missing 'Scoreboard' from apache server: ${*}" && return 1
+ [ -z "${apache_key_accesses}" ] && error "missing 'Total Accesses' from apache server: ${*}" && return 1
+ [ -z "${apache_key_kbytes}" ] && error "missing 'Total kBytes' from apache server: ${*}" && return 1
+ [ -z "${apache_key_reqpersec}" ] && error "missing 'ReqPerSec' from apache server: ${*}" && return 1
+ [ -z "${apache_key_bytespersec}" ] && error "missing 'BytesPerSec' from apache server: ${*}" && return 1
+ [ -z "${apache_key_bytesperreq}" ] && error "missing 'BytesPerReq' from apache server: ${*}" && return 1
+ [ -z "${apache_key_busyworkers}" ] && error "missing 'BusyWorkers' from apache server: ${*}" && return 1
+ [ -z "${apache_key_idleworkers}" ] && error "missing 'IdleWorkers' from apache server: ${*}" && return 1
+ [ -z "${apache_key_scoreboard}" ] && error "missing 'Scoreboard' from apache server: ${*}" && return 1
if [ ! -z "${apache_key_connstotal}" \
-a ! -z "${apache_key_connsasyncwriting}" \
apache_get() {
local oIFS="${IFS}" ret
- IFS=$':\n' apache_response=($(curl -Ss ${apache_curl_opts} "${apache_url}"))
+ IFS=$':\n' apache_response=($(run curl -Ss ${apache_curl_opts} "${apache_url}"))
ret=$?
IFS="${oIFS}"
-o -z "${apache_idleworkers}" \
]
then
- echo >&2 "apache: empty values got from apache server: ${apache_response[*]}"
+ error "empty values got from apache server: ${apache_response[*]}"
return 1
fi
apache_get
if [ $? -ne 0 ]
then
- echo >&2 "apache: cannot find stub_status on URL '${apache_url}'. Please set apache_url='http://apache.server:80/server-status?auto' in $confd/apache.conf"
+ error "cannot find stub_status on URL '${apache_url}'. Please set apache_url='http://apache.server:80/server-status?auto' in $confd/apache.conf"
return 1
fi
# no need for shebang - this file is loaded from charts.d.plugin
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+# GPL v3+
+#
+
apcupsd_ip=127.0.0.1
apcupsd_port=3551
apcupsd_priority=90000
apcupsd_get() {
- timeout $apcupsd_timeout apcaccess status "$1:$2"
+ run -t $apcupsd_timeout apcaccess status "$1:$2"
}
apcupsd_check() {
require_cmd apcaccess || return 1
- apcupsd_get $apcupsd_ip $apcupsd_port >/dev/null
+ run apcupsd_get $apcupsd_ip $apcupsd_port >/dev/null
if [ $? -ne 0 ]
then
- echo >&2 "apcupsd: ERROR: Cannot get information for apcupsd server."
+ error "cannot get information for apcupsd server."
return 1
elif [ $(apcupsd_get $apcupsd_ip $apcupsd_port | awk '/^STATUS.*/{ print $3 }') != "ONLINE" ]
then
- echo >&2 "apcupsd: ERROR: UPS not online."
+ error "APC UPS not online."
return 1
fi
print \"SET time = \" time;
print \"END\"
}"
- [ $? -ne 0 ] && echo >&2 "apcupsd: failed to get values" && return 1
+ [ $? -ne 0 ] && error "failed to get values" && return 1
return 0
}
# no need for shebang - this file is loaded from charts.d.plugin
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+# GPL v3+
+#
# THIS PLUGIN IS OBSOLETE
# USE apps.plugin INSTEAD
if [ -z "$cpu_apps_apps" ]
then
- echo >&2 "$PROGRAM_NAME: cpu_apps: Please set cpu_apps_apps='command1 command2 ...' in $confd/cpu_apps_apps.conf"
+ error "manual configuration required: please set cpu_apps_apps='command1 command2 ...' in $confd/cpu_apps_apps.conf"
return 1
fi
return 0
# no need for shebang - this file is loaded from charts.d.plugin
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+# GPL v3+
+#
+
# if this chart is called X.chart.sh, then all functions and global variables
# must start with X_
id="$( fixid "cpu$cpu" )"
- echo >&2 "charts.d: cpufreq: on file='$file', dir='$dir', cpu='$cpu', id='$id'"
+ debug "file='$file', dir='$dir', cpu='$cpu', id='$id'"
echo "DIMENSION $id '$id' absolute 1 1000"
echo >>$TMP_DIR/cpufreq.sh "echo \"SET $id = \"\$(< $file )"
echo >>$TMP_DIR/cpufreq.sh "echo END"
[ $cpufreq_source_update -eq 1 ] && echo >>$TMP_DIR/cpufreq.sh "}"
- # cat >&2 $TMP_DIR/cpufreq.sh
# ok, load the function cpufreq_update() we created
[ $cpufreq_source_update -eq 1 ] && . $TMP_DIR/cpufreq.sh
# no need for shebang - this file is loaded from charts.d.plugin
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+# GPL v3+
+#
+
# if this chart is called X.chart.sh, then all functions and global variables
# must start with X_
# - 1 to disable the chart
# check something
- [ "${example_magic_number}" != "12345" ] && echo >&2 "example: you have to set example_magic_number=$example_magic_number in example.conf to start example chart." && return 1
+ [ "${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
SET random = $example_value4
END
VALUESEOF
- # echo >&2 "example_count = $example_count value = $value4"
return 0
}
# no need for shebang - this file is loaded from charts.d.plugin
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+# GPL v3+
+#
+# Contributed by @jsveiga with PR #480
+
+# the exim command to run
exim_command=
# how frequently to collect queue size
exim_priority=60000
exim_check() {
- if [ -z "$exim_command" -o ! -x "$exim_command" ]
- then
- local d=
- for d in /sbin /usr/sbin /usr/local/sbin
- do
- if [ -x "$d/exim" ]
- then
- exim_command="$d/exim"
- break
- fi
- done
- fi
-
- if [ -z "$exim_command" -o ! -x "$exim_command" ]
- then
- echo >&2 "$PROGRAM_NAME: exim: cannot find exim executable. Please set 'exim_command=/path/to/exim' in $confd/exim.conf"
- return 1
- fi
+ if [ -z "${exim_command}" ]
+ then
+ require_cmd exim || return 1
+ exim_command="${EXIM_CMD}"
+ fi
- if [ `$exim_command -bpc 2>&1 | grep -c denied` -ne 0 ]
+ if [ $(${exim_command} -bpc 2>&1 | grep -c denied) -ne 0 ]
then
- echo >&2 "$PROGRAM_NAME: exim: permission denied. Please set 'queue_list_requires_admin = false' in your exim options file"
+ error "permission denied - please set 'queue_list_requires_admin = false' in your exim options file"
return 1
fi
}
exim_create() {
-cat <<EOF
+ cat <<EOF
CHART exim_local.qemails '' "Exim Queue Emails" "emails" queue exim.queued.emails line $((exim_priority + 1)) $exim_update_every
DIMENSION emails '' absolute 1 1
EOF
-return 0
+ return 0
}
exim_update() {
-echo "BEGIN exim_local.qemails $1"
-echo "SET emails = " `$exim_command -bpc`
-echo "END"
-return 0
+ echo "BEGIN exim_local.qemails $1"
+ echo "SET emails = " $(run ${exim_command} -bpc)
+ echo "END"
+ return 0
}
# no need for shebang - this file is loaded from charts.d.plugin
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+# GPL v3+
+#
+# contributed by @paulfantom with PR #511
+
# if this chart is called X.chart.sh, then all functions and global variables
# must start with X_
hddtemp_host="localhost"
# _check is called once, to find out if this chart should be enabled or not
hddtemp_check() {
- nc $hddtemp_host $hddtemp_port &>/dev/null && return 0 || return 1
+ require_cmd nc || return 1
+ run nc $hddtemp_host $hddtemp_port && return 0 || return 1
}
# _create is called once, to create the charts
# no need for shebang - this file is loaded from charts.d.plugin
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+# GPL v3+
+#
+
load_average_update_every=5
load_priority=100
# no need for shebang - this file is loaded from charts.d.plugin
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+# GPL v3+
+#
+
mem_apps_apps=
# these are required for computing memory in bytes and cpu in seconds
if [ -z "$mem_apps_apps" ]
then
- echo >&2 "$PROGRAM_NAME: mem_apps: not configured. Please set mem_apps_apps='command1 command2 ...' in $confd/mem_apps_apps.conf"
+ error "manual configuration required: please set mem_apps_apps='command1 command2 ...' in $confd/mem_apps_apps.conf"
return 1
fi
return 0
# no need for shebang - this file is loaded from charts.d.plugin
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+# GPL v3+
+#
+
# http://dev.mysql.com/doc/refman/5.0/en/server-status-variables.html
#
# https://dev.mysql.com/doc/refman/5.1/en/show-status.html
local oIFS="${IFS}"
mysql_data=()
IFS=$'\t'$'\n'
- #arr=($("${@}" -e "SHOW GLOBAL STATUS WHERE value REGEXP '^[0-9]';" | egrep "^(Bytes|Slow_|Que|Handl|Table|Selec|Sort_|Creat|Conne|Abort|Binlo|Threa|Innod|Qcach|Key_|Open)" ))
- #arr=($("${@}" -N -e "SHOW GLOBAL STATUS;" | egrep "^(Bytes|Slow_|Que|Handl|Table|Selec|Sort_|Creat|Conne|Abort|Binlo|Threa|Innod|Qcach|Key_|Open)[^ ]+\s[0-9]" ))
- arr=($("${@}" -N -e "SHOW GLOBAL STATUS;" | egrep "^(Bytes|Slow_|Que|Handl|Table|Selec|Sort_|Creat|Conne|Abort|Binlo|Threa|Innod|Qcach|Key_|Open)[^[:space:]]+[[:space:]]+[0-9]+" ))
+ #arr=($(run "${@}" -e "SHOW GLOBAL STATUS WHERE value REGEXP '^[0-9]';" | egrep "^(Bytes|Slow_|Que|Handl|Table|Selec|Sort_|Creat|Conne|Abort|Binlo|Threa|Innod|Qcach|Key_|Open)" ))
+ #arr=($(run "${@}" -N -e "SHOW GLOBAL STATUS;" | egrep "^(Bytes|Slow_|Que|Handl|Table|Selec|Sort_|Creat|Conne|Abort|Binlo|Threa|Innod|Qcach|Key_|Open)[^ ]+\s[0-9]" ))
+ arr=($(run "${@}" -N -e "SHOW GLOBAL STATUS;" | egrep "^(Bytes|Slow_|Que|Handl|Table|Selec|Sort_|Creat|Conne|Abort|Binlo|Threa|Innod|Qcach|Key_|Open)[^[:space:]]+[[:space:]]+[0-9]+" ))
IFS="${oIFS}"
[ "${#arr[@]}" -lt 3 ] && return 1
[ -z "${mysql_cmds[$m]}" ] && mysql_cmds[$m]="$mysql_cmd"
if [ -z "${mysql_cmds[$m]}" ]
then
- echo >&2 "$PROGRAM_NAME: mysql: cannot get mysql command for '$m'. Please set mysql_cmds[$m]='/path/to/mysql', in $confd/mysql.conf"
+ error "cannot get mysql command for '$m'. Please set mysql_cmds[$m]='/path/to/mysql', in $confd/mysql.conf"
fi
mysql_get "${mysql_cmds[$m]}" ${mysql_opts[$m]}
if [ ! $? -eq 0 ]
then
- echo >&2 "$PROGRAM_NAME: mysql: cannot get global status for '$m'. Please set mysql_opts[$m]='options' to whatever needed to get connected to the mysql server, in $confd/mysql.conf"
+ error "cannot get global status for '$m'. Please set mysql_opts[$m]='options' to whatever needed to get connected to the mysql server, in $confd/mysql.conf"
unset mysql_cmds[$m]
unset mysql_opts[$m]
unset mysql_ids[$m]
mysql_check tryroot "${@}"
return $?
else
- echo >&2 "$PROGRAM_NAME: mysql: no mysql servers found. Please set mysql_opts[name]='options' to whatever needed to get connected to the mysql server, in $confd/mysql.conf"
+ error "no mysql servers found. Please set mysql_opts[name]='options' to whatever needed to get connected to the mysql server, in $confd/mysql.conf"
return 1
fi
fi
unset mysql_ids[$m]
unset mysql_opts[$m]
unset mysql_cmds[$m]
- echo >&2 "$PROGRAM_NAME: mysql: failed to get values for '$m', disabling it."
+ error "failed to get values for '$m', disabling it."
continue
fi
fi
done
- [ ${#mysql_ids[@]} -eq 0 ] && echo >&2 "$PROGRAM_NAME: mysql: no mysql servers left active." && return 1
+ [ ${#mysql_ids[@]} -eq 0 ] && error "no mysql servers left active." && return 1
return 0
}
# no need for shebang - this file is loaded from charts.d.plugin
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+# GPL v3+
+#
+
# if this chart is called X.chart.sh, then all functions and global variables
# must start with X_
nginx_writing=0
nginx_waiting=0
nginx_get() {
- nginx_response=($(curl -Ss ${nginx_curl_opts} "${nginx_url}"))
+ nginx_response=($(run curl -Ss ${nginx_curl_opts} "${nginx_url}"))
[ $? -ne 0 -o "${#nginx_response[@]}" -eq 0 ] && return 1
if [ "${nginx_response[0]}" != "Active" \
-o "${nginx_response[14]}" != "Waiting:" \
]
then
- echo >&2 "nginx: Invalid response from nginx server: ${nginx_response[*]}"
+ error "Invalid response from nginx server: ${nginx_response[*]}"
return 1
fi
-o -z "${nginx_waiting}" \
]
then
- echo >&2 "nginx: empty values got from nginx server: ${nginx_response[*]}"
+ error "empty values got from nginx server: ${nginx_response[*]}"
return 1
fi
nginx_get
if [ $? -ne 0 ]
then
- echo >&2 "nginx: cannot find stub_status on URL '${nginx_url}'. Please set nginx_url='http://nginx.server/stub_status' in $confd/nginx.conf"
+ error "cannot find stub_status on URL '${nginx_url}'. Please set nginx_url='http://nginx.server/stub_status' in $confd/nginx.conf"
return 1
fi
# no need for shebang - this file is loaded from charts.d.plugin
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+# GPL v3+
+#
+
# a space separated list of UPS names
# if empty, the list returned by 'upsc -l' will be used
nut_ups=
declare -A nut_ids=()
nut_get_all() {
- timeout $nut_timeout upsc -l
+ run -t $nut_timeout upsc -l
}
nut_get() {
- timeout $nut_timeout upsc "$1"
+ run -t $nut_timeout upsc "$1"
}
nut_check() {
nut_ids[$x]="$( fixid "$x" )"
continue
fi
- echo >&2 "nut: ERROR: Cannot get information for NUT UPS '$x'."
+ error "cannot get information for NUT UPS '$x'."
done
if [ ${#nut_ids[@]} -eq 0 ]
then
- echo >&2 "nut: Please set nut_ups='ups_name' in $confd/nut.conf"
+ error "Cannot find UPSes - please set nut_ups='ups_name' in $confd/nut.conf"
return 1
fi
print \"SET temp = \" temp;
print \"END\"
}"
- [ $? -ne 0 ] && unset nut_ids[$i] && echo >&2 "nut: failed to get values for '$i', disabling it."
+ [ $? -ne 0 ] && unset nut_ids[$i] && error "failed to get values for '$i', disabling it."
done
- [ ${#nut_ids[@]} -eq 0 ] && echo >&2 "nut: no UPSes left active." && return 1
+ [ ${#nut_ids[@]} -eq 0 ] && error "no UPSes left active." && return 1
return 0
}
# no need for shebang - this file is loaded from charts.d.plugin
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+# GPL v3+
+#
+
opensips_opts="fifo get_statistics all"
opensips_cmd=
opensips_update_every=5
opensips_priority=80000
opensips_get_stats() {
- timeout $opensips_timeout "$opensips_cmd" $opensips_opts |\
+ 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" \
local x="$(opensips_get_stats | grep "^opensips_core_")"
if [ ! $? -eq 0 -o -z "$x" ]
then
- echo >&2 "$PROGRAM_NAME: opensips: cannot get global status. Please set opensips_opts='options' whatever needed to get connected to opensips server, in $confd/opensips.conf"
+ 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
eval "local $(opensips_get_stats)"
[ $? -ne 0 ] && return 1
- [ $opensips_command_failed -eq 1 ] && echo >&2 "$PROGRAM_NAME: opensips: failed to get values, disabling." && return 1
+ [ $opensips_command_failed -eq 1 ] && error "failed to get values, disabling." && return 1
# write the result of the work.
cat <<VALUESEOF
# no need for shebang - this file is loaded from charts.d.plugin
-# if this chart is called X.chart.sh, then all functions and global variables
-# must start with X_
-
-# first, you need open php-fpm status in php-fpm.conf
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+# GPL v3+
+#
+# Contributed by @safeie with PR #276
+
+# first, you need open php-fpm status in php-fpm.conf
# second, you need add status location in nginx.conf
# you can see, https://easyengine.io/tutorials/php/fpm-status-page/
phpfpm_get() {
local opts="${1}" url="${2}"
- phpfpm_response=($(curl -Ss ${opts} "${url}"))
+ phpfpm_response=($(run curl -Ss ${opts} "${url}"))
[ $? -ne 0 -o "${#phpfpm_response[@]}" -eq 0 ] && return 1
if [[ "${phpfpm_response[0]}" != "pool:" \
|| "${phpfpm_response[32]}" != "total" \
]]
then
- echo >&2 "phpfpm: invalid response from phpfpm status server: ${phpfpm_response[*]}"
+ error "invalid response from phpfpm status server: ${phpfpm_response[*]}"
return 1
fi
|| -z "${phpfpm_max_children_reached}" \
]]
then
- echo >&2 "phpfpm: empty values got from phpfpm status server: ${phpfpm_response[*]}"
+ error "empty values got from phpfpm status server: ${phpfpm_response[*]}"
return 1
fi
do
phpfpm_get "${phpfpm_curl_opts[$m]}" "${phpfpm_urls[$m]}"
if [ $? -ne 0 ]; then
- echo >&2 "phpfpm: cannot find status on URL '${phpfpm_url[$m]}'. Please set phpfpm_urls[$m]='http://localhost/status' in $confd/phpfpm.conf"
+ error "cannot find status on URL '${phpfpm_url[$m]}'. Please set phpfpm_urls[$m]='http://localhost/status' in $confd/phpfpm.conf"
unset phpfpm_urls[$m]
continue
fi
done
if [ ${#phpfpm_urls[@]} -eq 0 ]; then
- echo >&2 "phpfpm: no phpfpm servers found. Please set phpfpm_urls[name]='url' to whatever needed to get status to the phpfpm server, in $confd/phpfpm.conf"
+ error "no phpfpm servers found. Please set phpfpm_urls[name]='url' to whatever needed to get status to the phpfpm server, in $confd/phpfpm.conf"
return 1
fi
# no need for shebang - this file is loaded from charts.d.plugin
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+# GPL v3+
+#
+
# the postqueue command
# if empty, it will use the one found in the system path
postfix_postqueue=
if [ -z "$postfix_postqueue" -o ! -x "$postfix_postqueue" ]
then
postfix_postqueue="`which postqueue 2>/dev/null`"
- if [ -z "$postfix_postqueue" -o ! -x "$postfix_postqueue" ]
- then
- local d=
- for d in /sbin /usr/sbin /usr/local/sbin
- do
- if [ -x "$d/postqueue" ]
- then
- postfix_postqueue="$d/postqueue"
- break
- fi
- done
- fi
fi
if [ -z "$postfix_postqueue" -o ! -x "$postfix_postqueue" ]
then
- echo >&2 "$PROGRAM_NAME: postfix: cannot find postqueue. Please set 'postfix_postqueue=/path/to/postqueue' in $confd/postfix.conf"
+ error "cannot find postqueue. Please set 'postfix_postqueue=/path/to/postqueue' in $confd/postfix.conf"
return 1
fi
postfix_q_emails=0
postfix_q_size=0
- eval "`$postfix_postqueue -p |\
+ eval "$(run $postfix_postqueue -p |\
grep "^--" |\
sed -e "s/-- \([0-9]\+\) Kbytes in \([0-9]\+\) Requests.$/local postfix_q_size=\1\nlocal postfix_q_emails=\2/g" |\
- egrep "^local postfix_q_(emails|size)=[0-9]+$"`"
+ egrep "^local postfix_q_(emails|size)=[0-9]+$")"
# write the result of the work.
cat <<VALUESEOF
# no need for shebang - this file is loaded from charts.d.plugin
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+# GPL v3+
+#
+
# sensors docs
# https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface
# - 0 to enable the chart
# - 1 to disable the chart
- [ -z "$( sensors_find_all_files $sensors_sys_dir )" ] && echo >&2 "$PROGRAM_NAME: sensors: no sensors found in '$sensors_sys_dir'." && return 1
+ [ -z "$( sensors_find_all_files $sensors_sys_dir )" ] && error "no sensors found in '$sensors_sys_dir'." && return 1
return 0
}
[ $v -ne 0 ] && echo "$f" && continue
excluded=
- echo >&2 "$PROGRAM_NAME: sensors: $f gives zero values"
+ error "$f gives zero values"
done
}
v=$(( v + 1 - 1 ))
[ $v -ne 0 ] && echo "$f" && continue
- echo >&2 "$PROGRAM_NAME: sensors: $f is disabled"
+ error "$f is disabled"
done
}
id="$( fixid "$device.$subsystem.$dir" )"
- echo >&2 "charts.d: sensors: on path='$path', dir='$dir', device='$device', subsystem='$subsystem', id='$id', name='$name'"
+ debug "path='$path', dir='$dir', device='$device', subsystem='$subsystem', id='$id', name='$name'"
for mode in temperature voltage fans power current energy humidity
do
done
[ $sensors_source_update -eq 1 ] && echo >>$TMP_DIR/sensors.sh "}"
- # cat >&2 $TMP_DIR/sensors.sh
# ok, load the function sensors_update() we created
[ $sensors_source_update -eq 1 ] && . $TMP_DIR/sensors.sh
# no need for shebang - this file is loaded from charts.d.plugin
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+# GPL v3+
+#
+
squid_host=
squid_port=
squid_url=
squid_get_stats_internal() {
local host="$1" port="$2" url="$3"
- squidclient -h $host -p $port $url
+ run squidclient -h $host -p $port $url
}
squid_get_stats() {
squid_host="$host"
squid_port="$port"
squid_url="$url"
- echo >&2 "squid: found squid at '$host:$port' with url '$url'"
+ debug "found squid at '$host:$port' with url '$url'"
return 0
fi
done
done
- echo >&2 "squid: cannot find squid running in localhost. Please set squid_url='url' and squid_host='IP' and squid_port='PORT' in $confd/squid.conf"
+ error "cannot find squid running in localhost. Please set squid_url='url' and squid_host='IP' and squid_port='PORT' in $confd/squid.conf"
return 1
}
local x="$(squid_get_stats | grep client_http.requests)"
if [ ! $? -eq 0 -o -z "$x" ]
then
- echo >&2 "squid: cannot fetch URL '$squid_url' by connecting to $squid_host:$squid_port. Please set squid_url='url' and squid_host='host' and squid_port='port' in $confd/squid.conf"
+ error "cannot fetch URL '$squid_url' by connecting to $squid_host:$squid_port. Please set squid_url='url' and squid_host='host' and squid_port='port' in $confd/squid.conf"
return 1
fi
# no need for shebang - this file is loaded from charts.d.plugin
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+# GPL v3+
+#
+# Contributed by @jgeromero with PR #277
+
# Description: Tomcat netdata charts.d plugin
# Author: Jorge Romero
# check if url, username, passwords are set
if [ -z "${tomcat_url}" ]; then
- echo >&2 "tomcat url is unset or set to the empty string"
+ error "tomcat url is unset or set to the empty string"
return 1
fi
if [ -z "${tomcat_user}" ]; then
# check backwards compatibility
if [ -z "${tomcatUser}" ]; then
- echo >&2 "tomcat user is unset or set to the empty string"
+ error "tomcat user is unset or set to the empty string"
return 1
else
tomcat_user="${tomcatUser}"
if [ -z "${tomcat_password}" ]; then
# check backwards compatibility
if [ -z "${tomcatPassword}" ]; then
- echo >&2 "tomcat password is unset or set to the empty string"
+ error "tomcat password is unset or set to the empty string"
return 1
else
tomcat_password="${tomcatPassword}"
tomcat_get
if [ $? -ne 0 ]
then
- echo >&2 "tomcat: couldn't get to status page on URL '${tomcat_url}'."\
- "Please make sure tomcat url, username and password are correct."
+ error "cannot get to status page on URL '${tomcat_url}'. Please make sure tomcat url, username and password are correct."
return 1
fi
tomcat_get() {
# collect tomcat values
tomcat_port="$(IFS=/ read -ra a <<< "$tomcat_url"; hostport=${a[2]}; echo "${hostport#*:}")"
- mapfile -t lines < <(curl -u "$tomcat_user":"$tomcat_password" -Ss ${tomcat_curl_opts} "$tomcat_url" |\
- xmlstarlet sel \
+ mapfile -t lines < <(run curl -u "$tomcat_user":"$tomcat_password" -Ss ${tomcat_curl_opts} "$tomcat_url" |\
+ run xmlstarlet sel \
-t -m "/status/jvm/memory" -v @free \
-n -m "/status/connector[@name='\"http-bio-$tomcat_port\"']/threadInfo" -v @currentThreadCount \
-n -v @currentThreadsBusy \
dist_config_DATA = \
apps_groups.conf \
charts.d.conf \
+ fping.conf \
node.d.conf \
python.d.conf \
health_alarm_notify.conf \
--- /dev/null
+# This plugin requires a special version of fping.
+# Get it from https://github.com/ktsaou/fping
+# and build it, like this:
+#
+# cd /usr/src
+# git clone https://github.com/ktsaou/fping.git fping-netdata.git
+# cd fping-netdata.git
+# ./autogen.sh
+# ./configure --prefix=/usr/local
+# make
+# cp src/fping /usr/local/bin/
+# chown root:root /usr/local/bin/fping
+# chmod 4755 /usr/local/bin/fping
+#
+# -----------------------------------------------------------------------------
+# configuration options
+# can be overwritten at /etc/netdata/fping.conf
+
+# the fping binary to use
+# we need one that can output netdata friendly info
+fping="$(which fping || command -v fping)"
+
+# a space separated list of hosts to fping
+# it is best to put hostnames here
+hosts=""
+
+# the time in milliseconds (1 sec = 1000 ms)
+# to ping the hosts - by default 2 pings per iteration
+ping_every="$((update_every * 1000 / 2))"
+
+# how many retries to make if a host does not respond
+retries=1
['7cf6402b51e5070f2be3ad6fe059ff89']='charts.d.conf'
['a02d14124b19c635c1426cee2e98bac5']='charts.d.conf'
['ca026d7c779f0a7cb7787713c5be5c47']='charts.d.conf'
+ ['1f5545b3ff52b3eb75ee05401f67a9bc']='fping.conf'
+ ['2fa8fb929fd597f2ab97b6efc540a043']='health_alarm_notify.conf'
+ ['42ad0e70b1365b6e7244cc305dbaa529']='health_alarm_notify.conf'
+ ['707a63f53f4b32e01d134ae90ba94aad']='health_alarm_notify.conf'
+ ['8fd472a854b0996327e8ed3562161182']='health_alarm_notify.conf'
+ ['a55133f1b0be0a4255057849dd451b09']='health_alarm_notify.conf'
+ ['a89c516a1144435a88decf25509318ac']='health_alarm_notify.conf'
+ ['b68706bb8101ef85192db92f865a5d80']='health_alarm_notify.conf'
+ ['c080e006f544c949baca33cc24a9c126']='health_alarm_notify.conf'
+ ['ce2e8768964a936f58c4c2144aee8a01']='health_alarm_notify.conf'
+ ['e3023092e3b2bbb5351e0fe6682f4fe9']='health_alarm_notify.conf'
+ ['f8dade4484f1b6a48655388502df7d5a']='health_alarm_notify.conf'
+ ['fd3164e6e8cb6726706267eae49aa082']='health_alarm_notify.conf'
+ ['ff1b3d8ae8b2149c711d8da9b7a9c4bd']='health_alarm_notify.conf'
['074df527cc70b5f38c0714f08f20e57c']='health.d/apache.conf'
['174c21a6ce5de97bda83d502aa47a9f8']='health.d/apache.conf'
['3848172053221b95279ba9bf789cd4e0']='health.d/apache.conf'
['ca08a9b18d38ae0a0f5081a7cdc96863']='health.d/swap.conf'
['da29d2ab1ab7b8fda189960c840e5144']='health.d/swap.conf'
['b3fc4749b132e55ac0d3a0f92859237e']='health.d/tcp_resets.conf'
- ['2fa8fb929fd597f2ab97b6efc540a043']='health_alarm_notify.conf'
- ['42ad0e70b1365b6e7244cc305dbaa529']='health_alarm_notify.conf'
- ['707a63f53f4b32e01d134ae90ba94aad']='health_alarm_notify.conf'
- ['8fd472a854b0996327e8ed3562161182']='health_alarm_notify.conf'
- ['a55133f1b0be0a4255057849dd451b09']='health_alarm_notify.conf'
- ['a89c516a1144435a88decf25509318ac']='health_alarm_notify.conf'
- ['b68706bb8101ef85192db92f865a5d80']='health_alarm_notify.conf'
- ['c080e006f544c949baca33cc24a9c126']='health_alarm_notify.conf'
- ['ce2e8768964a936f58c4c2144aee8a01']='health_alarm_notify.conf'
- ['e3023092e3b2bbb5351e0fe6682f4fe9']='health_alarm_notify.conf'
- ['f8dade4484f1b6a48655388502df7d5a']='health_alarm_notify.conf'
- ['fd3164e6e8cb6726706267eae49aa082']='health_alarm_notify.conf'
- ['ff1b3d8ae8b2149c711d8da9b7a9c4bd']='health_alarm_notify.conf'
['707a63f53f4b32e01d134ae90ba94aad']='health_email_recipients.conf'
['a752e51d923e15add4a11fa8f3be935a']='health_email_recipients.conf'
['73a8e10dfe4183aca751e9e2a80dabe3']='node.d.conf'
+ ['6b917300747e7e8314844237e2462261']='python.d/apache_cache.conf'
+ ['e0e96cc47ed61d6492416be5236cd4d3']='python.d/apache_cache.conf'
+ ['5278ebbae19c60db600f0a119cb3664e']='python.d/apache.conf'
+ ['5829812db29598db5857c9f433e96fef']='python.d/apache.conf'
+ ['6bf0de6e3b251b765b10a71d8c5c319d']='python.d/apache.conf'
['13141998a5d71308d9c119834c27bfd3']='python.d.conf'
['38d1bf04fe9901481dd6febcc0404a86']='python.d.conf'
['4b775fb31342f1478b3773d041a72911']='python.d.conf'
['bba2f3886587f137ea08a6e63dd3d376']='python.d.conf'
['d55be5bb5e108da1e7645da007c53cd4']='python.d.conf'
['f82924563e41d99cdae5431f0af69155']='python.d.conf'
- ['5278ebbae19c60db600f0a119cb3664e']='python.d/apache.conf'
- ['5829812db29598db5857c9f433e96fef']='python.d/apache.conf'
- ['6bf0de6e3b251b765b10a71d8c5c319d']='python.d/apache.conf'
- ['6b917300747e7e8314844237e2462261']='python.d/apache_cache.conf'
- ['e0e96cc47ed61d6492416be5236cd4d3']='python.d/apache_cache.conf'
['7830066c46a7e5f9682b8d3f4566b4e5']='python.d/cpufreq.conf'
['b5b5a8d6d991fb1cef8d80afa23ba114']='python.d/cpufreq.conf'
['dc0d2b96378f290eec3fcf98b89ad824']='python.d/cpufreq.conf'
--- /dev/null
+#!/usr/bin/env bash
+
+path=$(dirname "$0")
+cd "${path}" || exit 1
+
+if [ ! -f "plantuml.jar" ]
+then
+ echo >&2 "Please download 'plantuml.jar' from http://plantuml.com/ and put it the same folder with me."
+ exit 1
+fi
+
+for x in *.puml
+do
+ [ "${x}" = "config.puml" ] && continue
+
+ echo >&2 "Working on ${x}..."
+ java -jar plantuml.jar -tpng "${x}"
+ java -jar plantuml.jar -tsvg "${x}"
+ # java -jar plantuml.jar -ttxt "${x}"
+done
--- /dev/null
+
+skinparam handwritten true
+skinparam monochrome true
+skinparam roundcorner 15
+
+skinparam sequence {
+ ArrowThickness 3
+
+ DividerFontColor Black
+ DividerFontName Comic Sans MS
+ DividerFontSize 15
+ DividerFontStyle Italic
+
+ DelayFontColor Black
+ DelayFontName Comic Sans MS
+ DelayFontSize 15
+ DelayFontStyle Italic
+
+ TitleFontColor Black
+ TitleFontName Comic Sans MS
+ TitleFontStyle Italic
+ TitleFontSize 25
+
+ ArrowColor DeepSkyBlue
+ ArrowFontColor Black
+ ArrowFontName Comic Sans MS
+ ArrowFontStyle Regular
+ ArrowFontSize 19
+
+ ActorBorderColor DeepSkyBlue
+
+ LifeLineBorderColor blue
+ LifeLineBackgroundColor #A9DCDF
+
+ ParticipantBorderColor DeepSkyBlue
+ ParticipantBackgroundColor LightBlue
+ ParticipantFontName Comic Sans MS
+ ParticipantFontSize 20
+ ParticipantFontColor Black
+
+ ActorBackgroundColor aqua
+ ActorFontColor Black
+ ActorFontSize 20
+ ActorFontName Comic Sans MS
+}
+
--- /dev/null
+@startuml
+!include config.puml
+
+title netdata registry operation
+actor "web browser" as user
+participant "netdata 1" as n1
+participant "registry 1" as r1
+autonumber "<b>0."
+
+== standard dashboard communication ==
+
+user ->n1 : \
+ hi, give me the dashboard
+
+n1 --> user : \
+ welcome, here it is...
+
+... a few seconds later ...
+
+== registry related communication ==
+
+user -> n1 : \
+ now give me registry information
+
+n1 --> user: \
+ here it is, talk to <b>registry 1</b>
+
+note left of r1 #eee: \
+ only your web browser \n\
+ talks to the registry
+
+user -> r1 : \
+ Hey <b>registry 1</b>, \
+I am accessing <b>netdata 1</b>...
+
+r1 --> user : \
+ nice!, here are other netdata servers \
+you have accessed in the past
+
+@enduml
LC_ALL=C
umask 022
+# Be nice on production environments
+renice 19 $$ >/dev/null 2>/dev/null
+
+processors=$(cat /proc/cpuinfo | grep ^processor | wc -l)
+[ $(( processors )) -lt 1 ] && processors=1
+
# you can set CFLAGS before running installer
CFLAGS="${CFLAGS--O3}"
echo >configs.signatures.tmp
- for x in $(find conf.d -name \*.conf)
+ for x in $(find conf.d -name \*.conf | sort)
do
x="${x/conf.d\//}"
echo "${x}"
fi
echo >&2 "Compiling netdata ..."
-run make || exit 1
+run make -j${processors} || exit 1
if [ "${BASH_VERSINFO[0]}" -ge "4" ]
then
}
stop_netdata_on_pid() {
- local pid="$1" ret=0 count=0
+ local pid="${1}" ret=0 count=0
- isnetdata $pid || return 0
+ isnetdata ${pid} || return 0
- printf >&2 "Stopping netdata on pid $pid ..."
- while [ ! -z "$pid" -a $ret -eq 0 ]
+ printf >&2 "Stopping netdata on pid ${pid} ..."
+ while [ ! -z "$pid" -a ${ret} -eq 0 ]
do
- if [ $count -gt 45 ]
+ if [ ${count} -gt 45 ]
then
- echo >&2 "Cannot stop the running netdata on pid $pid."
+ echo >&2 "Cannot stop the running netdata on pid ${pid}."
return 1
fi
count=$(( count + 1 ))
- run kill $pid 2>/dev/null
+ run kill ${pid} 2>/dev/null
ret=$?
- test $ret -eq 0 && printf >&2 "." && sleep 2
+ test ${ret} -eq 0 && printf >&2 "." && sleep 2
done
echo >&2
- if [ $ret -eq 0 ]
+ if [ ${ret} -eq 0 ]
then
- echo >&2 "SORRY! CANNOT STOP netdata ON PID $pid !"
+ echo >&2 "SORRY! CANNOT STOP netdata ON PID ${pid} !"
return 1
fi
- echo >&2 "netdata on pid $pid stopped."
+ echo >&2 "netdata on pid ${pid} stopped."
return 0
}
$(cat /var/run/netdata/netdata.pid 2>/dev/null) \
$(pidof netdata 2>/dev/null)
do
- stop_netdata_on_pid $p
+ stop_netdata_on_pid ${p}
done
}
ret=$?
# try curl
- if [ $ret -ne 0 -o ! -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" ]
+ if [ ${ret} -ne 0 -o ! -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" ]
then
curl -s -o "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" "http://localhost:${NETDATA_PORT}/netdata.conf"
ret=$?
fi
- if [ $ret -eq 0 -a -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" ]
+ if [ ${ret} -eq 0 -a -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" ]
then
mv "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" "${NETDATA_PREFIX}/etc/netdata/netdata.conf"
echo >&2 "New configuration saved for you to edit at ${NETDATA_PREFIX}/etc/netdata/netdata.conf"
License: GPL v3+
Group: Applications/System
Source0: http://firehol.org/download/netdata/releases/v@PACKAGE_VERSION@/%{name}-@PACKAGE_VERSION@.tar.xz
-URL: http://netdata.firehol.org/
+URL: http://my-netdata.io/
BuildRequires: pkgconfig
BuildRequires: xz
BuildRequires: zlib-devel
BuildRequires: libuuid-devel
+Requires: zlib
+Requires: libuuid
# Packages can be found in the EPEL repo
%if %{with nfacct}
BuildRequires: libmnl-devel
BuildRequires: libnetfilter_acct-devel
+Requires: libmnl
+Requires: libnetfilter_acct
%endif
Requires(pre): /usr/sbin/groupadd
%dir %{_sysconfdir}/%{name}
%config(noreplace) %{_sysconfdir}/%{name}/*.conf
+#%config(noreplace) %{_sysconfdir}/%{name}/charts.d/*.conf
%config(noreplace) %{_sysconfdir}/%{name}/health.d/*.conf
+#%config(noreplace) %{_sysconfdir}/%{name}/node.d/*.conf
%config(noreplace) %{_sysconfdir}/%{name}/python.d/*.conf
%config(noreplace) %{_sysconfdir}/logrotate.d/%{name}
'use strict';
+// netdata
+// real-time performance and health monitoring, done right!
+// (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+// GPL v3+
+
var url = require('url');
var http = require('http');
var util = require('util');
});
req.on('error', function(e) {
- service.error('Failed to make request: ' + netdata.stringify(service.request) + ', message: ' + e.message);
+ if(netdata.options.DEBUG === true) netdata.debug('Failed to make request: ' + netdata.stringify(service.request) + ', message: ' + e.message);
+ service.error('Failed to make request, message: ' + e.message);
callback(null);
});
return util.inspect(obj, {depth: 10});
},
+ zeropad2: function(s) {
+ if(typeof s !== 'string')
+ s = s.toString();
+
+ switch(s.length) {
+ case 0: return '00';
+ case 1: return '0' + s;
+ default: return s;
+ }
+ },
+
+ logdate: function(d) {
+ if(typeof d === 'undefined') d = new Date();
+ return this.zeropad2(d.getFullYear()) + '-' + this.zeropad2(d.getMonth()) + '-' + this.zeropad2(d.getDay())
+ + ' ' + this.zeropad2(d.getHours()) + ':' + this.zeropad2(d.getMinutes()) + ':' + this.zeropad2(d.getSeconds());
+ },
+
// show debug info, if debug is enabled
debug: function(msg) {
if(this.options.DEBUG === true) {
- var now = new Date();
- console.error(now.toString() + ': ' + netdata.options.filename + ': DEBUG: ' + ((typeof(msg) === 'object')?netdata.stringify(msg):msg).toString());
+ console.error(this.logdate() + ': ' + netdata.options.filename + ': DEBUG: ' + ((typeof(msg) === 'object')?netdata.stringify(msg):msg).toString());
}
},
// log an error
error: function(msg) {
- var now = new Date();
- console.error(now.toString() + ': ' + netdata.options.filename + ': ERROR: ' + ((typeof(msg) === 'object')?netdata.stringify(msg):msg).toString());
+ console.error(this.logdate() + ': ' + netdata.options.filename + ': ERROR: ' + ((typeof(msg) === 'object')?netdata.stringify(msg):msg).toString());
},
// send data to netdata
cgroup-name.sh \
charts.d.dryrun-helper.sh \
charts.d.plugin \
+ fping.plugin \
node.d.plugin \
python.d.plugin \
tc-qos-helper.sh \
# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
# GPL v3+
#
-# Script the send alarm notifications for netdata
+# Script to send alarm notifications for netdata
#
# Features:
# - multiple notification methods
# - telegram.org notifications
#
-me="${0}"
+export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/sbin"
+export LC_ALL=C
+
+# -----------------------------------------------------------------------------
+
+PROGRAM_NAME="$(basename "${0}")"
+
+logdate() {
+ date "+%Y-%m-%d %H:%M:%S"
+}
+
+log() {
+ local status="${1}"
+ shift
+
+ echo >&2 "$(logdate): ${PROGRAM_NAME}: ${status}: ${*}"
+
+}
+
+warning() {
+ log WARNING "${@}"
+}
+
+error() {
+ log ERROR "${@}"
+}
+
+info() {
+ log INFO "${@}"
+}
+
+fatal() {
+ log FATAL "${@}"
+ exit 1
+}
+
+debug=0
+debug() {
+ [ $debug -eq 1 ] && log DEBUG "${@}"
+}
+
+# -----------------------------------------------------------------------------
# check for BASH v4+ (required for associative arrays)
[ $(( ${BASH_VERSINFO[0]} )) -lt 4 ] && \
- echo >&2 "${me}: BASH version 4 or later is required (this is ${BASH_VERSION})." && \
- exit 1
+ fatal "BASH version 4 or later is required (this is ${BASH_VERSION})."
+# -----------------------------------------------------------------------------
# defaults to allow running this script by hand
+
NETDATA_CONFIG_DIR="${NETDATA_CONFIG_DIR-/etc/netdata}"
NETDATA_CACHE_DIR="${NETDATA_CACHE_DIR-/var/cache/netdata}"
[ -z "${NETDATA_REGISTRY_URL}" ] && NETDATA_REGISTRY_URL="https://registry.my-netdata.io"
# don't do anything if this is not WARNING, CRITICAL or CLEAR
if [ "${status}" != "WARNING" -a "${status}" != "CRITICAL" -a "${status}" != "CLEAR" ]
then
- echo >&2 "${me}: not sending notification for ${status} on '${chart}.${name}'"
+ info "not sending notification for ${status} on '${chart}.${name}'"
exit 1
fi
# don't do anything if this is CLEAR, but it was not WARNING or CRITICAL
if [ "${old_status}" != "WARNING" -a "${old_status}" != "CRITICAL" -a "${status}" = "CLEAR" ]
then
- echo >&2 "${me}: not sending notification for ${status} on '${chart}.${name}' (last status was ${old_status})"
+ info "not sending notification for ${status} on '${chart}.${name}' (last status was ${old_status})"
exit 1
fi
# check that we have at least a method enabled
if [ "${SEND_EMAIL}" != "YES" -a "${SEND_PUSHOVER}" != "YES" -a "${SEND_TELEGRAM}" != "YES" -a "${SEND_SLACK}" != "YES" ]
then
- echo >&2 "All notification methods are disabled. Not sending a notification."
- exit 1
+ fatal "All notification methods are disabled. Not sending a notification."
fi
# -----------------------------------------------------------------------------
if [ $ret -eq 0 ]
then
- echo >&2 "${me}: Sent email notification for: ${host} ${chart}.${name} is ${status} to '${to_email}'"
+ info "sent email notification for: ${host} ${chart}.${name} is ${status} to '${to_email}'"
return 0
else
- echo >&2 "${me}: Failed to send email notification for: ${host} ${chart}.${name} is ${status} to '${to_email}' with error code ${ret}."
+ error "failed to send email notification for: ${host} ${chart}.${name} is ${status} to '${to_email}' with error code ${ret}."
return 1
fi
fi
if [ "${httpcode}" == "200" ]
then
- echo >&2 "${me}: Sent pushover notification for: ${host} ${chart}.${name} is ${status} to '${user}'"
+ info "sent pushover notification for: ${host} ${chart}.${name} is ${status} to '${user}'"
sent=$((sent + 1))
else
- echo >&2 "${me}: Failed to send pushover notification for: ${host} ${chart}.${name} is ${status} to '${user}' with HTTP error code ${httpcode}."
+ error "failed to send pushover notification for: ${host} ${chart}.${name} is ${status} to '${user}' with HTTP error code ${httpcode}."
fi
done
if [ "${httpcode}" == "200" ]
then
- echo >&2 "${me}: Sent telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}'"
+ info "sent telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}'"
sent=$((sent + 1))
elif [ "${httpcode}" == "401" ]
then
- echo >&2 "${me}: Failed to send telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}': Wrong bot token."
+ error "failed to send telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}': Wrong bot token."
else
- echo >&2 "${me}: Failed to send telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}' with HTTP error code ${httpcode}."
+ error "failed to send telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}' with HTTP error code ${httpcode}."
fi
done
httpcode=$(${curl} --write-out %{http_code} --silent --output /dev/null -X POST --data-urlencode "payload=${payload}" "${webhook}")
if [ "${httpcode}" == "200" ]
then
- echo >&2 "${me}: Sent slack notification for: ${host} ${chart}.${name} is ${status} to '${channel}'"
+ info "sent slack notification for: ${host} ${chart}.${name} is ${status} to '${channel}'"
sent=$((sent + 1))
else
- echo >&2 "${me}: Failed to send slack notification for: ${host} ${chart}.${name} is ${status} to '${channel}', with HTTP error code ${httpcode}."
+ error "failed to send slack notification for: ${host} ${chart}.${name} is ${status} to '${channel}', with HTTP error code ${httpcode}."
fi
done
#!/usr/bin/env bash
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+# GPL v3+
+#
+# Script to find a better name for cgroups
+#
+
export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/sbin"
export LC_ALL=C
+# -----------------------------------------------------------------------------
+
+PROGRAM_NAME="$(basename "${0}")"
+
+logdate() {
+ date "+%Y-%m-%d %H:%M:%S"
+}
+
+log() {
+ local status="${1}"
+ shift
+
+ echo >&2 "$(logdate): ${PROGRAM_NAME}: ${status}: ${*}"
+
+}
+
+warning() {
+ log WARNING "${@}"
+}
+
+error() {
+ log ERROR "${@}"
+}
+
+info() {
+ log INFO "${@}"
+}
+
+fatal() {
+ log FATAL "${@}"
+ exit 1
+}
+
+debug=0
+debug() {
+ [ $debug -eq 1 ] && log DEBUG "${@}"
+}
+
+# -----------------------------------------------------------------------------
+
NETDATA_CONFIG_DIR="${NETDATA_CONFIG_DIR-/etc/netdata}"
CONFIG="${NETDATA_CONFIG_DIR}/cgroups-names.conf"
CGROUP="${1}"
NAME=
+# -----------------------------------------------------------------------------
+
if [ -z "${CGROUP}" ]
then
- echo >&2 "${0}: called without a cgroup name. Nothing to do."
- exit 1
+ fatal "called without a cgroup name. Nothing to do."
fi
if [ -f "${CONFIG}" ]
NAME="$(grep "^${CGROUP} " "${CONFIG}" | sed "s/[[:space:]]\+/ /g" | cut -d ' ' -f 2)"
if [ -z "${NAME}" ]
then
- echo >&2 "${0}: cannot find cgroup '${CGROUP}' in '${CONFIG}'."
+ info "cannot find cgroup '${CGROUP}' in '${CONFIG}'."
fi
#else
-# echo >&2 "${0}: configuration file '${CONFIG}' is not available."
+# info "configuration file '${CONFIG}' is not available."
fi
function get_name_classic {
local DOCKERID="$1"
- echo >&2 "Running command: docker ps --filter=id=\"${DOCKERID}\" --format=\"{{.Names}}\""
+ info "Running command: docker ps --filter=id=\"${DOCKERID}\" --format=\"{{.Names}}\""
NAME="$( docker ps --filter=id="${DOCKERID}" --format="{{.Names}}" )"
return 0
}
local DOCKERID="$1"
if [ ! -S "/var/run/docker.sock" ]
then
- echo >&2 "Can't find /var/run/docker.sock"
+ warning "Can't find /var/run/docker.sock"
return 1
fi
- echo >&2 "Running API command: /containers/${DOCKERID}/json"
+ info "Running API command: /containers/${DOCKERID}/json"
JSON=$(echo -e "GET /containers/${DOCKERID}/json HTTP/1.0\r\n" | nc -U /var/run/docker.sock | egrep '^{.*')
NAME=$(echo $JSON | jq -r .Name,.Config.Hostname | grep -v null | head -n1 | sed 's|^/||')
return 0
fi
if [ -z "${NAME}" ]
then
- echo >&2 "Cannot find the name of docker container '${DOCKERID}'"
+ warning "cannot find the name of docker container '${DOCKERID}'"
NAME="${DOCKERID:0:12}"
else
- echo >&2 "Docker container '${DOCKERID}' is named '${NAME}'"
+ info "docker container '${DOCKERID}' is named '${NAME}'"
fi
fi
fi
[ ${#NAME} -gt 100 ] && NAME="${NAME:0:100}"
fi
-echo >&2 "${0}: cgroup '${CGROUP}' is called '${NAME}'"
+info "cgroup '${CGROUP}' is called '${NAME}'"
echo "${NAME}"
#!/usr/bin/env bash
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 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"
+
PROGRAM_FILE="$0"
PROGRAM_NAME="$(basename $0)"
PROGRAM_NAME="${PROGRAM_NAME/.plugin}"
+MODULE_NAME="main"
-# if you need to run parallel charts.d processes
-# just link this files with a different name
-# in the same directory, with a .plugin suffix
-# netdata will start multiple of them
-# each will have a different config file
+# -----------------------------------------------------------------------------
+# create temp dir
-echo >&2 "$PROGRAM_NAME: started from '$PROGRAM_FILE' with options: $*"
+debug=0
+TMP_DIR=
+chartsd_cleanup() {
+ 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
+ exit 0
+}
+trap chartsd_cleanup EXIT
+trap chartsd_cleanup SIGHUP
+trap chartsd_cleanup INT
-if [ $(( ${BASH_VERSINFO[0]} )) -lt 4 ]
+if [ $UID = "0" ]
then
- echo >&2
- echo >&2 "$PROGRAM_NAME: ERROR"
- echo >&2 "BASH version 4 or later is required."
- echo >&2 "You are running version: ${BASH_VERSION}"
- echo >&2 "Please upgrade."
- echo >&2
- exit 1
+ 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"
+}
+
+log() {
+ local status="${1}"
+ shift
+
+ echo >&2 "$(logdate): ${PROGRAM_NAME}: ${status}: ${MODULE_NAME}: ${*}"
+
+}
+
+warning() {
+ log WARNING "${@}"
+}
+
+error() {
+ log ERROR "${@}"
+}
+
+info() {
+ log INFO "${@}"
+}
+
+fatal() {
+ log FATAL "${@}"
+ echo "DISABLE"
+ exit 1
+}
+
+debug() {
+ [ $debug -eq 1 ] && log DEBUG "${@}"
+}
+
+# -----------------------------------------------------------------------------
# check a few commands
+
require_cmd() {
- which "$1" >/dev/null
- if [ $? -ne 0 ]
+ local x=$(which "${1}" 2>/dev/null || command -v "${1}" 2>/dev/null)
+ if [ -z "${x}" -o ! -x "${x}" ]
then
- echo >&2 "$PROGRAM_NAME: ERROR: Command '$1' is not found in the system path."
+ warning "command '${1}' is not found in ${PATH}."
+ eval "${1^^}_CMD=\"\""
return 1
fi
+
+ eval "${1^^}_CMD=\"${x}\""
return 0
}
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: $*"
# -----------------------------------------------------------------------------
-# insternal defaults
+# internal defaults
# netdata exposes a few environment variables for us
pluginsd="${NETDATA_PLUGINS_DIR}"
# -----------------------------------------------------------------------------
# parse parameters
-debug=0
check=0
chart_only=
while [ ! -z "$1" ]
continue
fi
- echo >&2 "Cannot understand parameter $1. Aborting."
- echo "DISABLE"
- exit 1
+ fatal "Cannot understand parameter $1. Aborting."
done
if [ -f "$myconfig" ]
then
. "$myconfig"
- if [ $? -ne 0 ]
- then
- echo >&2 "$PROGRAM_NAME: cannot load $myconfig"
- echo "DISABLE"
- exit 1
- fi
+ [ $? -ne 0 ] && fatal "cannot load $myconfig"
+
time_divisor=$((time_divisor))
[ $time_divisor -lt 10 ] && time_divisor=10
[ $time_divisor -gt 100 ] && time_divisor=100
else
- echo >&2 "$PROGRAM_NAME: configuration file '$myconfig' not found. Using defaults."
+ info "configuration file '$myconfig' not found. Using defaults."
fi
# we check for the timeout command, after we load our
test $update_every -eq 0 && update_every=1 # if it is zero, make it 1
# check the charts.d directory
-if [ ! -d "$chartsd" ]
- then
- echo >&2 "$PROGRAM_NAME: cannot find charts directory '$chartsd'"
- echo "DISABLE"
-fi
-
+[ ! -d "$chartsd" ] && fatal "cannot find charts directory '$chartsd'"
# -----------------------------------------------------------------------------
# library functions
tr "[A-Z]" "[a-z]"
}
+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:\n --- BEGIN TRACE ---\n"
+ cat "${TMP_DIR}/run.${pid}"
+ printf " --- END TRACE ---\n"
+ } >&2
+ fi
+ rm "${TMP_DIR}/run.${pid}"
+
+ return ${ret}
+}
+
# convert any floating point number
# to integer, give a multiplier
# the result is stored in ${FLOAT2INT_RESULT}
local f m="$2" a b l v=($1)
f=${v[0]}
- # echo >&2 "value='${1}' f='${f}', m='${m}'"
-
# the length of the multiplier - 1
l=$(( ${#m} - 1 ))
# store the result
FLOAT2INT_RESULT=$(( (a * m) + b ))
- #echo >&2 "FLOAT2INT_RESULT='${FLOAT2INT_RESULT}'"
}
all_charts() {
cd "$chartsd"
- [ $? -ne 0 ] && echo >&2 "$PROGRAM_NAME: Cannot cd to $chartsd" && return 1
+ [ $? -ne 0 ] && error "cannot cd to $chartsd" && return 1
ls *.chart.sh | sed "s/\.chart\.sh$//g"
}
for chart in $( all_charts )
do
+ MODULE_NAME="${chart}"
+
eval "enabled=\$$chart"
if [ -z "${enabled}" ]
then
if [ ! "${enabled}" = "${required}" ]
then
- echo >&2 "$PROGRAM_NAME: '$chart' is NOT enabled. Add a line with $chart=$required in $myconfig to enable it (or remove the line that disables it)."
+ info "is disabled. Add a line with $chart=$required in $myconfig to enable it (or remove the line that disables it)."
else
- [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: '$chart' is enabled."
+ 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
- echo >&2 "$PROGRAM_NAME: chart '$chart' does not seem to have a $chart$charts_check() function. Disabling it."
+ 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
- echo >&2 "$PROGRAM_NAME: chart '$chart' does not seem to have a $chart$charts_create() function. Disabling it."
+ 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
- echo >&2 "$PROGRAM_NAME: chart '$chart' does not seem to have a $chart$charts_update() function. Disabling it."
+ error "module '$chart' does not seem to have a $chart$charts_update() function. Disabling it."
continue
fi
#then
# if [ ! -z "$( cat "$confd/$chart.conf" | sed "s/^ \+//g" | grep -v "^$" | grep -v "^#" | grep -v "^$chart$charts_undescore" )" ]
# then
- # echo >&2 "$PROGRAM_NAME: chart's $chart config $confd/$chart.conf should only have lines starting with $chart$charts_undescore . Disabling it."
+ # error "module's $chart config $confd/$chart.conf should only have lines starting with $chart$charts_undescore . Disabling it."
# continue
# fi
#fi
# "$pluginsd/charts.d.dryrun-helper.sh" "$chart" "$chartsd/$chart.chart.sh" "$confd/$chart.conf" >/dev/null
# if [ $? -ne 0 ]
# then
- # echo >&2 "$PROGRAM_NAME: chart's $chart did not pass the dry run check. This means it uses global variables not starting with $chart. Disabling it."
+ # 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 -eq 1 ] && echo >&2 "$PROGRAM_NAME: enabled charts: $charts2"
+ debug "enabled charts: $charts2"
}
-
# -----------------------------------------------------------------------------
# load the charts
active_charts=
for chart in $( all_enabled_charts )
do
- [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: loading chart: '$chartsd/$chart.chart.sh'"
+ MODULE_NAME="${chart}"
+
+ debug "loading module: '$chartsd/$chart.chart.sh'"
+
. "$chartsd/$chart.chart.sh"
- if [ -f "$confd/charts.d/$chart.conf" ]
+ if [ -f "$confd/$PROGRAM_NAME/$chart.conf" ]
then
- [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: loading chart options: '$confd/charts.d/$chart.conf'"
- . "$confd/charts.d/$chart.conf"
+ debug "loading module configuration: '$confd/$PROGRAM_NAME/$chart.conf'"
+ . "$confd/$PROGRAM_NAME/$chart.conf"
elif [ -f "$confd/$chart.conf" ]
then
- [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: loading chart options: '$confd/$chart.conf'"
+ debug "loading module configuration: '$confd/$chart.conf'"
. "$confd/$chart.conf"
else
- echo >&2 "$PROGRAM_NAME: $chart: configuration file '$confd/charts.d/$chart.conf' not found. Using defaults."
+ warning "configuration file '$confd/$PROGRAM_NAME/$chart.conf' not found. Using defaults."
fi
eval "dt=\$$chart$suffix_update_every"
$chart$charts_check
if [ $? -eq 0 ]
then
- [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: '$chart' activated"
+ debug "module '$chart' activated"
active_charts="$active_charts $chart"
else
- echo >&2 "$PROGRAM_NAME: chart '$chart' check() function reports failure."
+ error "module's '$chart' check() function reports failure."
fi
done
-[ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: activated charts: $active_charts"
+MODULE_NAME="main"
+debug "activated modules: $active_charts"
# -----------------------------------------------------------------------------
# if we only need a specific chart, remove all the others
if [ ! -z "${chart_only}" ]
then
- [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: requested to run only for: '${chart_only}'"
+ debug "requested to run only for: '${chart_only}'"
check_charts=
for chart in $active_charts
do
done
active_charts="$check_charts"
fi
-[ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: activated charts: $active_charts"
+debug "activated charts: $active_charts"
# stop if we just need a pre-check
if [ $check -eq 1 ]
then
- echo >&2 "CHECK RESULT"
- echo >&2 "Will run the charts: $active_charts"
+ info "CHECK RESULT"
+ info "Will run the charts: $active_charts"
exit 0
fi
# -----------------------------------------------------------------------------
-# create temp dir
-
-TMP_DIR=
-chartsd_cleanup() {
- cd /tmp
- 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
- exit 0
-}
-trap chartsd_cleanup EXIT
-trap chartsd_cleanup SIGHUP
-trap chartsd_cleanup INT
-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
-
-cd "$TMP_DIR" || exit 1
+cd "${TMP_DIR}" || exit 1
# -----------------------------------------------------------------------------
# create charts
run_charts=
for chart in $active_charts
do
- [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: Calling '$chart$charts_create()'..."
+ MODULE_NAME="${chart}"
+
+ debug "calling '$chart$charts_create()'..."
$chart$charts_create
if [ $? -eq 0 ]
then
run_charts="$run_charts $chart"
- [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: '$chart' has initialized."
+ debug "'$chart' initialized."
else
- echo >&2 "$PROGRAM_NAME: chart '$chart' function '$chart$charts_create()' reports failure."
+ error "module's '$chart' function '$chart$charts_create()' reports failure."
fi
done
-[ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: run_charts='$run_charts'"
+MODULE_NAME="main"
+debug "run_charts='$run_charts'"
# -----------------------------------------------------------------------------
# update dimensions
-if [ -z "$run_charts" ]
- then
- echo >&2 "$PROGRAM_NAME: No charts to collect data from."
- echo "DISABLE"
- exit 1
-fi
+[ -z "$run_charts" ] && fatal "No charts to collect data from."
declare -A charts_last_update=() charts_update_every=() charts_next_update=() charts_run_counter=() charts_serial_failures=()
global_update() {
for chart in "${now_charts[@]}"
do
- #echo >&2 " DEBUG: chart: $chart last: ${charts_last_update[$chart]}, next: ${charts_next_update[$chart]}, now: ${now_ms}"
+ MODULE_NAME="${chart}"
+
if [ ${now_ms} -ge ${charts_next_update[$chart]} ]
then
last_ms=${charts_last_update[$chart]}
dt=$(( (now_ms - last_ms) ))
- #echo >&2 " DEBUG: chart: $chart last: ${charts_last_update[$chart]}, next: ${charts_next_update[$chart]}, now: ${now_ms}, dt: ${dt}"
charts_last_update[$chart]=${now_ms}
fi
exec_start_ms=$now_ms
- #echo >&2 " EXEC: $chart$charts_update $dt"
$chart$charts_update $dt
ret=$?
if [ ${charts_serial_failures[$chart]} -gt 10 ]
then
- echo >&2 "$PROGRAM_NAME: chart '$chart' update() function reported failure ${charts_serial_failures[$chart]} times. Disabling it."
+ error "module's '$chart' update() function reported failure ${charts_serial_failures[$chart]} times. Disabling it."
else
- echo >&2 "$PROGRAM_NAME: chart '$chart' update() function reports failure. Will keep trying for a while."
+ error "module's '$chart' update() function reports failure. Will keep trying for a while."
next_charts+=($chart)
fi
fi
next_charts+=($chart)
fi
done
+ MODULE_NAME="${chart}"
# wait the time you are required to
next_ms=$((now_ms + (update_every * 1000 * 100) ))
millis="0${millis}"
fi
- [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: sleeping for ${seconds}.${millis} seconds."
+ debug "sleeping for ${seconds}.${millis} seconds."
${mysleep} ${seconds}.${millis}
else
- [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: sleeping for ${update_every} seconds."
+ debug "sleeping for ${update_every} seconds."
${mysleep} $update_every
fi
test ${now_ms} -ge ${exit_at} && exit 0
done
- echo >&2 "$PROGRAM_NAME: Nothing left to do. Disabling charts.d.plugin."
- echo "DISABLE"
+ fatal "nothing left to do, exiting..."
}
global_update
--- /dev/null
+#!/usr/bin/env bash
+
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+# GPL v3+
+#
+# This plugin requires a special version of fping.
+# Get it from https://github.com/ktsaou/fping
+# and build it, like this:
+#
+# cd /usr/src
+# git clone https://github.com/ktsaou/fping.git fping-netdata.git
+# cd fping-netdata.git
+# ./autogen.sh
+# ./configure --prefix=/usr/local
+# make
+# cp src/fping /usr/local/bin/
+# chown root:root /usr/local/bin/fping
+# chmod 4755 /usr/local/bin/fping
+#
+# Then, create /etc/netdata/fping.conf
+# and set the configuration options given below
+
+export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/sbin"
+export LC_ALL=C
+
+# -----------------------------------------------------------------------------
+
+PROGRAM_NAME="$(basename "${0}")"
+
+logdate() {
+ date "+%Y-%m-%d %H:%M:%S"
+}
+
+log() {
+ local status="${1}"
+ shift
+
+ echo >&2 "$(logdate): ${PROGRAM_NAME}: ${status}: ${*}"
+
+}
+
+warning() {
+ log WARNING "${@}"
+}
+
+error() {
+ log ERROR "${@}"
+}
+
+info() {
+ log INFO "${@}"
+}
+
+fatal() {
+ log FATAL "${@}"
+ echo "DISABLE"
+ exit 1
+}
+
+debug=0
+debug() {
+ [ $debug -eq 1 ] && log DEBUG "${@}"
+}
+
+# -----------------------------------------------------------------------------
+
+# the frequency to send info to netdata
+# passed by netdata as the first parameter
+update_every="${1-1}"
+
+# the netdata configuration directory
+# passed by netdata as an environment variable
+NETDATA_CONFIG_DIR="${NETDATA_CONFIG_DIR-/etc/netdata}"
+
+# -----------------------------------------------------------------------------
+# configuration options
+# can be overwritten at /etc/netdata/fping.conf
+
+# the fping binary to use
+# we need one that can output netdata friendly info
+fping="$(which fping || command -v fping)"
+
+# a space separated list of hosts to fping
+hosts=""
+
+# the time in milliseconds (1 sec = 1000 ms)
+# to ping the hosts - by default 2 pings per iteration
+ping_every="$((update_every * 1000 / 2))"
+
+# how many retries to make if a host does not respond
+retries=1
+
+# -----------------------------------------------------------------------------
+# load the configuration file
+
+if [ ! -f "${NETDATA_CONFIG_DIR}/fping.conf" ]
+then
+ fatal "configuration file '${NETDATA_CONFIG_DIR}/fping.conf' not found - nothing to do."
+fi
+
+source "${NETDATA_CONFIG_DIR}/fping.conf"
+
+if [ -z "${hosts}" ]
+then
+ fatal "no hosts configued in '${NETDATA_CONFIG_DIR}/fping.conf' - nothing to do."
+fi
+
+if [ -z "${fping}" -o ! -x "${fping}" ]
+then
+ fatal "command '${fping}' is not executable - cannot proceed."
+fi
+
+# the fping options we will use
+options=( -N -l -R -Q ${update_every} -p ${ping_every} -r ${retries} ${hosts} )
+
+# execute fping
+exec "${fping}" "${options[@]}"
+
+# if we cannot execute fping, stop
+fatal "command '${fping} ${options[@]}' failed to be executed."
// Then, the second line, finds nodejs or node or js in the system path
// and executes it with the shell parameters.
+// netdata
+// real-time performance and health monitoring, done right!
+// (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+// GPL v3+
+
// --------------------------------------------------------------------------------------------------------------------
'use strict';
#!/usr/bin/env bash
+# netdata
+# real-time performance and health monitoring, done right!
+# (C) 2016 Costa Tsaousis <costa@tsaousis.gr>
+# GPL v3+
+#
+# This script is a helper to allow netdata collect tc data
+# parsing tc output has been implemented in C, inside netdata
+# This script allows setting names to dimensions.
+
export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/sbin"
+export LC_ALL=C
PROGRAM_FILE="$0"
PROGRAM_NAME="$(basename $0)"
PROGRAM_NAME="${PROGRAM_NAME/.plugin}"
+# -----------------------------------------------------------------------------
+
+logdate() {
+ date "+%Y-%m-%d %H:%M:%S"
+}
+
+log() {
+ local status="${1}"
+ shift
+
+ echo >&2 "$(logdate): ${PROGRAM_NAME}: ${status}: ${*}"
+
+}
+
+warning() {
+ log WARNING "${@}"
+}
+
+error() {
+ log ERROR "${@}"
+}
+
+info() {
+ log INFO "${@}"
+}
+
+fatal() {
+ log FATAL "${@}"
+ exit 1
+}
+
+debug=0
+debug() {
+ [ $debug -eq 1 ] && log DEBUG "${@}"
+}
+
+# -----------------------------------------------------------------------------
+
plugins_dir="${NETDATA_PLUGINS_DIR}"
[ -z "$plugins_dir" ] && plugins_dir="$( dirname $PROGRAM_FILE )"
if [ -z "${tc}" -o ! -x "${tc}" ]
then
- echo >&2 "${PROGRAM_NAME}: Cannot find command 'tc' in this system."
- exit 1
+ fatal "cannot find command 'tc' in this system."
fi
devices=
LOG_COUNTER -= 1
now = time()
if LOG_COUNTER >= 0:
- timestamp = strftime('%y-%m-%d %X')
+ timestamp = strftime('%Y-%m-%d %X')
msg = "%s: %s %s: %s" % (timestamp, PROGRAM, str(msg_type), " ".join(args))
WRITE(msg + "\n")
FLUSH()
if NEXT_CHECK <= now:
NEXT_CHECK = now - (now % LOG_INTERVAL) + LOG_INTERVAL
if LOG_COUNTER < 0:
- timestamp = strftime('%y-%m-%d %X')
+ timestamp = strftime('%Y-%m-%d %X')
msg = "%s: Prevented %s log messages from displaying" % (timestamp, str(0 - LOG_COUNTER))
WRITE(msg + "\n")
FLUSH()
while (nanosleep(&req, &rem) == -1) {
if (likely(errno == EINTR)) {
- info("nanosleep() interrupted (while sleeping for %llu microseconds).", usec);
+ debug(D_SYSTEM, "nanosleep() interrupted (while sleeping for %llu microseconds).", usec);
req.tv_sec = rem.tv_sec;
req.tv_nsec = rem.tv_nsec;
} else {
procfile_close(ff);
- info("System has %d processors.", processors);
+ debug(D_SYSTEM, "System has %d processors.", processors);
return processors;
}
}
procfile_close(ff);
- info("System supports %d pids.", pid_max);
+ debug(D_SYSTEM, "System supports %d pids.", pid_max);
return pid_max;
}
#include <time.h>
#include <unistd.h>
#include <uuid/uuid.h>
+#include <mntent.h>
#ifdef STORAGE_WITH_MATH
#include <math.h>
if(!done)
error("Cannot adjust my Out-Of-Memory score to %d.", score);
else
- info("Adjusted my Out-Of-Memory score to %d.", score);
+ debug(D_SYSTEM, "Adjusted my Out-Of-Memory score to %d.", score);
}
int sched_setscheduler_idle(void) {
if(i != 0)
error("Cannot adjust my scheduling priority to IDLE.");
else
- info("Adjusted my scheduling priority to IDLE.");
+ debug(D_SYSTEM, "Adjusted my scheduling priority to IDLE.");
return i;
#else
// never become a problem
if(sched_setscheduler_idle() != 0) {
if(nice(19) == -1) error("Cannot lower my CPU priority.");
- else info("Set my nice value to 19.");
+ else debug(D_SYSTEM, "Set my nice value to 19.");
}
if(user && *user) {
if(become_user(user, pidfd) != 0) {
error("Cannot become user '%s'. Continuing as we are.", user);
}
- else info("Successfully became user '%s'.", user);
+ else debug(D_SYSTEM, "Successfully became user '%s'.", user);
}
if(pidfd != -1) {
const char *health_default_exec;
const char *health_default_recipient;
const char *log_filename;
+ size_t log_entries_written;
FILE *log_fp;
};
.health_default_exec = PLUGINS_DIR "/alarm-notify.sh",
.health_default_recipient = "root",
.log_filename = VARLIB_DIR "/health/alarm_log.db",
+ .log_entries_written = 0,
.log_fp = NULL
};
if(health.log_fp) {
if (setvbuf(health.log_fp, NULL, _IOLBF, 0) != 0)
- error("Cannot set line buffering on health log file.");
+ error("Health: cannot set line buffering on health log file.");
return 0;
}
- error("Cannot open health log file '%s'. Health data will be lost in case of netdata or server crash.", health.log_filename);
+ error("Health: cannot open health log file '%s'. Health data will be lost in case of netdata or server crash.", health.log_filename);
return -1;
}
}
}
-static inline void health_log_recreate(void) {
- if(health.log_fp != NULL) {
+static inline void health_log_rotate(void) {
+ static size_t rotate_every = 0;
+
+ if(unlikely(rotate_every == 0)) {
+ rotate_every = (size_t)config_get_number("health", "rotate log every lines", 2000);
+ if(rotate_every < 100) rotate_every = 100;
+ }
+
+ if(unlikely(health.log_entries_written > rotate_every)) {
health_alarm_log_close();
+ char old_filename[FILENAME_MAX + 1];
+ snprintfz(old_filename, FILENAME_MAX, "%s.old", health.log_filename);
+
+ if(unlink(old_filename) == -1 && errno != ENOENT)
+ error("Health: cannot remove old alarms log file '%s'", old_filename);
+
+ if(link(health.log_filename, old_filename) == -1 && errno != ENOENT)
+ error("Health: cannot move file '%s' to '%s'.", health.log_filename, old_filename);
+
+ if(unlink(health.log_filename) == -1 && errno != ENOENT)
+ error("Health: cannot remove old alarms log file '%s'", health.log_filename);
+
// open it with truncate
health.log_fp = fopen(health.log_filename, "w");
- if(health.log_fp) fclose(health.log_fp);
- else error("Cannot truncate health log '%s'", health.log_filename);
+
+ if(health.log_fp)
+ fclose(health.log_fp);
+ else
+ error("Health: cannot truncate health log '%s'", health.log_filename);
health.log_fp = NULL;
+ health.log_entries_written = 0;
health_alarm_log_open();
}
}
static inline void health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae) {
- (void)host;
- (void)ae;
-
-/* if(likely(health.log_fp)) {
- if(unlikely(fprintf(health.log_fp, "A\t%s\t%08x\t%08x\t%08x\t%08x\t%08x\t%08x\t%s\t%s\t%s\t%s\t%s\t%08x\n",
- host->hostname,
- ae->unique_id,
- ae->alarm_id,
- ae->alarm_event_id,
- (uint32_t)ae->when,
- (uint32_t)ae->duration,
- (uint32_t)ae->non_clear_duration,
- (uint32_t)ae->exec_run_timestamp,
- ae->name,
- ae->chart,
- ae->family,
- ae->exec,
- ae->recipient
- ) < 0))
+ health_log_rotate();
+
+ if(likely(health.log_fp)) {
+ if(unlikely(fprintf(health.log_fp
+ , "%c\t%s"
+ "\t%08x\t%08x\t%08x\t%08x\t%08x"
+ "\t%08x\t%08x\t%08x"
+ "\t%08x\t%08x\t%08x"
+ "\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s"
+ "\t%d\t%d\t%d\t%d"
+ "\t%Lf\t%Lf"
+ "\n"
+ , (ae->flags & HEALTH_ENTRY_FLAG_SAVED)?'U':'A'
+ , host->hostname
+
+ , ae->unique_id
+ , ae->alarm_id
+ , ae->alarm_event_id
+ , ae->updated_by_id
+ , ae->updates_id
+
+ , (uint32_t)ae->when
+ , (uint32_t)ae->duration
+ , (uint32_t)ae->non_clear_duration
+ , (uint32_t)ae->flags
+ , (uint32_t)ae->exec_run_timestamp
+ , (uint32_t)ae->delay_up_to_timestamp
+
+ , (ae->name)?ae->name:""
+ , (ae->chart)?ae->chart:""
+ , (ae->family)?ae->family:""
+ , (ae->exec)?ae->exec:""
+ , (ae->recipient)?ae->recipient:""
+ , (ae->source)?ae->source:""
+ , (ae->units)?ae->units:""
+ , (ae->info)?ae->info:""
+
+ , ae->exec_code
+ , ae->new_status
+ , ae->old_status
+ , ae->delay
+
+ , (long double)ae->new_value
+ , (long double)ae->old_value
+ ) < 0))
error("Health: failed to save alarm log entry. Health data may be lost in case of abnormal restart.");
+ else {
+ ae->flags |= HEALTH_ENTRY_FLAG_SAVED;
+ health.log_entries_written++;
+ }
}
-*/
+}
+
+static inline ssize_t health_alarm_log_read(RRDHOST *host, FILE *fp, const char *filename) {
+ static uint32_t max_unique_id = 0, max_alarm_id = 0;
+ ssize_t loaded = -1, updated = -1, errored = -1, duplicate = -1;
+
+ errno = 0;
+
+ char *s, *buf = mallocz(65536 + 1);
+ size_t line = 0, len = 0;
+ loaded = updated = errored = duplicate = 0;
+
+ pthread_rwlock_rdlock(&host->health_log.alarm_log_rwlock);
+
+ while((s = fgets_trim_len(buf, 65536, fp, &len))) {
+ health.log_entries_written++;
+ line++;
+
+ int max_entries = 30, entries = 0;
+ char *pointers[max_entries];
+
+ pointers[entries++] = s++;
+ while(*s) {
+ if(unlikely(*s == '\t')) {
+ *s = '\0';
+ pointers[entries++] = ++s;
+ if(entries >= max_entries) {
+ error("Health: line %zu of file '%s' has more than %d entries. Ignoring excessive entries.", line, filename, max_entries);
+ break;
+ }
+ }
+ else s++;
+ }
+
+ if(likely(*pointers[0] == 'U' || *pointers[0] == 'A')) {
+ ALARM_ENTRY *ae = NULL;
+
+ if(entries < 26) {
+ error("Health: line %zu of file '%s' should have at least 26 entries, but it has %d. Ignoring it.", line, filename, entries);
+ errored++;
+ continue;
+ }
+
+ // check that we have valid ids
+ uint32_t unique_id = (uint32_t)strtoul(pointers[2], NULL, 16);
+ if(!unique_id) {
+ error("Health: line %zu of file '%s' states alarm entry with invalid unique id %u (%s). Ignoring it.", line, filename, unique_id, pointers[2]);
+ errored++;
+ continue;
+ }
+
+ uint32_t alarm_id = (uint32_t)strtoul(pointers[3], NULL, 16);
+ if(!alarm_id) {
+ error("Health: line %zu of file '%s' states alarm entry for invalid alarm id %u (%s). Ignoring it.", line, filename, alarm_id, pointers[3]);
+ errored++;
+ continue;
+ }
+
+ if(unlikely(*pointers[0] == 'A')) {
+ // make sure it is properly numbered
+ if(unlikely(host->health_log.alarms && unique_id < host->health_log.alarms->unique_id)) {
+ error("Health: line %zu of file '%s' has alarm log entry with %u in wrong order. Ignoring it.", line, filename, unique_id);
+ errored++;
+ continue;
+ }
+
+ ae = callocz(1, sizeof(ALARM_ENTRY));
+ }
+ else if(unlikely(*pointers[0] == 'U')) {
+ // find the original
+ for(ae = host->health_log.alarms; ae; ae = ae->next) {
+ if(unlikely(unique_id == ae->unique_id)) {
+ if(unlikely(*pointers[0] == 'A')) {
+ error("Health: line %zu of file '%s' adds duplicate alarm log entry with unique id %u. Using the later."
+ , line, filename, unique_id);
+ *pointers[0] = 'U';
+ duplicate++;
+ }
+ break;
+ }
+ else if(unlikely(unique_id > ae->unique_id)) {
+ // no need to continue
+ // the linked list is sorted
+ ae = NULL;
+ break;
+ }
+ }
+
+ // if not found, skip this line
+ if(!ae) {
+ // error("Health: line %zu of file '%s' updates alarm log entry with unique id %u, but it is not found.", line, filename, unique_id);
+ continue;
+ }
+ }
+
+ // check for a possible host missmatch
+ //if(strcmp(pointers[1], host->hostname))
+ // error("Health: line %zu of file '%s' provides an alarm for host '%s' but this is named '%s'.", line, filename, pointers[1], host->hostname);
+
+ ae->unique_id = unique_id;
+ ae->alarm_id = alarm_id;
+ ae->alarm_event_id = (uint32_t)strtoul(pointers[4], NULL, 16);
+ ae->updated_by_id = (uint32_t)strtoul(pointers[5], NULL, 16);
+ ae->updates_id = (uint32_t)strtoul(pointers[6], NULL, 16);
+
+ ae->when = (uint32_t)strtoul(pointers[7], NULL, 16);
+ ae->duration = (uint32_t)strtoul(pointers[8], NULL, 16);
+ ae->non_clear_duration = (uint32_t)strtoul(pointers[9], NULL, 16);
+
+ ae->flags = (uint32_t)strtoul(pointers[10], NULL, 16);
+ ae->flags |= HEALTH_ENTRY_FLAG_SAVED;
+
+ ae->exec_run_timestamp = (uint32_t)strtoul(pointers[11], NULL, 16);
+ ae->delay_up_to_timestamp = (uint32_t)strtoul(pointers[12], NULL, 16);
+
+ if(unlikely(ae->name)) freez(ae->name);
+ ae->name = strdupz(pointers[13]);
+ ae->hash_name = simple_hash(ae->name);
+
+ if(unlikely(ae->chart)) freez(ae->chart);
+ ae->chart = strdupz(pointers[14]);
+ ae->hash_chart = simple_hash(ae->chart);
+
+ if(unlikely(ae->family)) freez(ae->family);
+ ae->family = strdupz(pointers[15]);
+
+ if(unlikely(ae->exec)) freez(ae->exec);
+ ae->exec = strdupz(pointers[16]);
+ if(!*ae->exec) { freez(ae->exec); ae->exec = NULL; }
+
+ if(unlikely(ae->recipient)) freez(ae->recipient);
+ ae->recipient = strdupz(pointers[17]);
+ if(!*ae->recipient) { freez(ae->recipient); ae->recipient = NULL; }
+
+ if(unlikely(ae->source)) freez(ae->source);
+ ae->source = strdupz(pointers[18]);
+ if(!*ae->source) { freez(ae->source); ae->source = NULL; }
+
+ if(unlikely(ae->units)) freez(ae->units);
+ ae->units = strdupz(pointers[19]);
+ if(!*ae->units) { freez(ae->units); ae->units = NULL; }
+
+ if(unlikely(ae->info)) freez(ae->info);
+ ae->info = strdupz(pointers[20]);
+ if(!*ae->info) { freez(ae->info); ae->info = NULL; }
+
+ ae->exec_code = atoi(pointers[21]);
+ ae->new_status = atoi(pointers[22]);
+ ae->old_status = atoi(pointers[23]);
+ ae->delay = atoi(pointers[24]);
+
+ ae->new_value = strtold(pointers[25], NULL);
+ ae->old_value = strtold(pointers[26], NULL);
+
+ // add it to host if not already there
+ if(unlikely(*pointers[0] == 'A')) {
+ ae->next = host->health_log.alarms;
+ host->health_log.alarms = ae;
+ loaded++;
+ }
+ else updated++;
+
+ if(unlikely(ae->unique_id > max_unique_id))
+ max_unique_id = ae->unique_id;
+
+ if(unlikely(ae->alarm_id >= max_alarm_id))
+ max_alarm_id = ae->alarm_id;
+ }
+ else {
+ error("Health: line %zu of file '%s' is invalid (unrecognized entry type '%s').", line, filename, pointers[0]);
+ errored++;
+ }
+ }
+
+ pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock);
+
+ freez(buf);
+
+ if(!max_unique_id) max_unique_id = (uint32_t)time(NULL);
+ if(!max_alarm_id) max_alarm_id = (uint32_t)time(NULL);
+
+ host->health_log.next_log_id = max_unique_id + 1;
+ host->health_log.next_alarm_id = max_alarm_id + 1;
+
+ debug(D_HEALTH, "Health: loaded file '%s' with %zd new alarm entries, updated %zd alarms, errors %zd entries, duplicate %zd", filename, loaded, updated, errored, duplicate);
+ return loaded;
}
static inline void health_alarm_log_load(RRDHOST *host) {
- (void)host;
+ health_alarm_log_close();
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s.old", health.log_filename);
+ FILE *fp = fopen(filename, "r");
+ if(!fp)
+ error("Health: cannot open health file: %s", filename);
+ else {
+ health_alarm_log_read(host, fp, filename);
+ fclose(fp);
+ }
+
+ health.log_entries_written = 0;
+ fp = fopen(health.log_filename, "r");
+ if(!fp)
+ error("Health: cannot open health file: %s", health.log_filename);
+ else {
+ health_alarm_log_read(host, fp, health.log_filename);
+ fclose(fp);
+ }
+
+ health_alarm_log_open();
}
+
// ----------------------------------------------------------------------------
// health alarm log management
ALARM_ENTRY *t;
for(t = host->health_log.alarms ; t ; t = t->next) {
if(t != ae && t->alarm_id == ae->alarm_id) {
- if(!(t->notifications & HEALTH_ENTRY_NOTIFICATIONS_UPDATED) && !t->updated_by_id) {
- t->notifications |= HEALTH_ENTRY_NOTIFICATIONS_UPDATED;
+ if(!(t->flags & HEALTH_ENTRY_FLAG_UPDATED) && !t->updated_by_id) {
+ t->flags |= HEALTH_ENTRY_FLAG_UPDATED;
t->updated_by_id = ae->unique_id;
ae->updates_id = t->unique_id;
health_alarm_log_save(host, t);
}
- else {
- // no need to continue
- break;
- }
+
+ // no need to continue
+ break;
}
}
pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock);
return 1;
}
+static inline char *tabs2spaces(char *s) {
+ char *t = s;
+ while(*t) {
+ if(unlikely(*t == '\t')) *t = ' ';
+ t++;
+ }
+
+ return s;
+}
+
static inline char *health_source_file(size_t line, const char *path, const char *filename) {
char buffer[FILENAME_MAX + 1];
snprintfz(buffer, FILENAME_MAX, "%zu@%s/%s", line, path, filename);
while((s = fgets(&buffer[append], (int)(HEALTH_CONF_MAX_LINE - append), fp)) || append) {
int stop_appending = !s;
line++;
- // info("Line %zu of file '%s/%s': '%s'", line, path, filename, s);
s = trim(buffer);
if(!s) continue;
- // info("Trimmed line %zu of file '%s/%s': '%s'", line, path, filename, s);
append = strlen(s);
if(!stop_appending && s[append - 1] == '\\') {
continue;
}
- // info("Health file '%s/%s', key '%s', value '%s'", path, filename, key, value);
uint32_t hash = simple_uhash(key);
if(hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) {
rc = callocz(1, sizeof(RRDCALC));
rc->next_event_id = 1;
- rc->name = strdupz(value);
+ rc->name = tabs2spaces(strdupz(value));
rc->hash = simple_hash(rc->name);
rc->source = health_source_file(line, path, filename);
rc->green = NAN;
rrdcalctemplate_free(&localhost, rt);
rt = callocz(1, sizeof(RRDCALCTEMPLATE));
- rt->name = strdupz(value);
+ rt->name = tabs2spaces(strdupz(value));
rt->hash_name = simple_hash(rt->name);
rt->source = health_source_file(line, path, filename);
rt->green = NAN;
if(hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) {
if(rc->chart) {
if(strcmp(rc->chart, value))
- info("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+ error("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, path, filename, rc->name, key, rc->chart, value, value);
freez(rc->chart);
}
- rc->chart = strdupz(value);
+ rc->chart = tabs2spaces(strdupz(value));
rc->hash_chart = simple_hash(rc->chart);
}
else if(hash == hash_lookup && !strcasecmp(key, HEALTH_LOOKUP_KEY)) {
}
else if(hash == hash_every && !strcasecmp(key, HEALTH_EVERY_KEY)) {
if(!health_parse_duration(value, &rc->update_every))
- info("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' cannot parse duration: '%s'.",
+ error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' cannot parse duration: '%s'.",
line, path, filename, rc->name, key, value);
}
else if(hash == hash_green && !strcasecmp(key, HEALTH_GREEN_KEY)) {
char *e;
rc->green = strtold(value, &e);
if(e && *e) {
- info("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.",
+ error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.",
line, path, filename, rc->name, key, e);
}
}
char *e;
rc->red = strtold(value, &e);
if(e && *e) {
- info("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.",
+ error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.",
line, path, filename, rc->name, key, e);
}
}
else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) {
if(rc->exec) {
if(strcmp(rc->exec, value))
- info("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+ error("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, path, filename, rc->name, key, rc->exec, value, value);
freez(rc->exec);
}
- rc->exec = strdupz(value);
+ rc->exec = tabs2spaces(strdupz(value));
}
else if(hash == hash_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) {
if(rc->recipient) {
if(strcmp(rc->recipient, value))
- info("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+ error("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, path, filename, rc->name, key, rc->recipient, value, value);
freez(rc->recipient);
}
- rc->recipient = strdupz(value);
+ rc->recipient = tabs2spaces(strdupz(value));
}
else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) {
if(rc->units) {
if(strcmp(rc->units, value))
- info("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+ error("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, path, filename, rc->name, key, rc->units, value, value);
freez(rc->units);
}
- rc->units = strdupz(value);
+ rc->units = tabs2spaces(strdupz(value));
strip_quotes(rc->units);
}
else if(hash == hash_info && !strcasecmp(key, HEALTH_INFO_KEY)) {
if(rc->info) {
if(strcmp(rc->info, value))
- info("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+ error("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, path, filename, rc->name, key, rc->info, value, value);
freez(rc->info);
}
- rc->info = strdupz(value);
+ rc->info = tabs2spaces(strdupz(value));
strip_quotes(rc->info);
}
else if(hash == hash_delay && !strcasecmp(key, HEALTH_DELAY_KEY)) {
if(hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) {
if(rt->context) {
if(strcmp(rt->context, value))
- info("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+ error("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, path, filename, rt->name, key, rt->context, value, value);
freez(rt->context);
}
- rt->context = strdupz(value);
+ rt->context = tabs2spaces(strdupz(value));
rt->hash_context = simple_hash(rt->context);
}
else if(hash == hash_lookup && !strcasecmp(key, HEALTH_LOOKUP_KEY)) {
}
else if(hash == hash_every && !strcasecmp(key, HEALTH_EVERY_KEY)) {
if(!health_parse_duration(value, &rt->update_every))
- info("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' cannot parse duration: '%s'.",
+ error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' cannot parse duration: '%s'.",
line, path, filename, rt->name, key, value);
}
else if(hash == hash_green && !strcasecmp(key, HEALTH_GREEN_KEY)) {
char *e;
rt->green = strtold(value, &e);
if(e && *e) {
- info("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.",
+ error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.",
line, path, filename, rt->name, key, e);
}
}
char *e;
rt->red = strtold(value, &e);
if(e && *e) {
- info("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.",
+ error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.",
line, path, filename, rt->name, key, e);
}
}
else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) {
if(rt->exec) {
if(strcmp(rt->exec, value))
- info("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+ error("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, path, filename, rt->name, key, rt->exec, value, value);
freez(rt->exec);
}
- rt->exec = strdupz(value);
+ rt->exec = tabs2spaces(strdupz(value));
}
else if(hash == hash_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) {
if(rt->recipient) {
if(strcmp(rt->recipient, value))
- info("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+ error("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, path, filename, rt->name, key, rt->recipient, value, value);
freez(rt->recipient);
}
- rt->recipient = strdupz(value);
+ rt->recipient = tabs2spaces(strdupz(value));
}
else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) {
if(rt->units) {
if(strcmp(rt->units, value))
- info("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+ error("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, path, filename, rt->name, key, rt->units, value, value);
freez(rt->units);
}
- rt->units = strdupz(value);
+ rt->units = tabs2spaces(strdupz(value));
strip_quotes(rt->units);
}
else if(hash == hash_info && !strcasecmp(key, HEALTH_INFO_KEY)) {
if(rt->info) {
if(strcmp(rt->info, value))
- info("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+ error("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, path, filename, rt->name, key, rt->info, value, value);
freez(rt->info);
}
- rt->info = strdupz(value);
+ rt->info = tabs2spaces(strdupz(value));
strip_quotes(rt->info);
}
else if(hash == hash_delay && !strcasecmp(key, HEALTH_DELAY_KEY)) {
return;
}
+ char *pathname = config_get("health", "health db directory", VARLIB_DIR "/health");
+ if(mkdir(pathname, 0770) == -1 && errno != EEXIST)
+ fatal("Cannot create directory '%s'.", pathname);
+
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s/health-log.db", pathname);
+ health.log_filename = config_get("health", "health db file", filename);
+
health_alarm_log_load(&localhost);
+ health_alarm_log_open();
char *path = health_config_dir();
ae->name,
ae->chart,
ae->family,
- (ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_PROCESSED)?"true":"false",
- (ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_UPDATED)?"true":"false",
+ (ae->flags & HEALTH_ENTRY_FLAG_PROCESSED)?"true":"false",
+ (ae->flags & HEALTH_ENTRY_FLAG_UPDATED)?"true":"false",
(unsigned long)ae->exec_run_timestamp,
- (ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_EXEC_FAILED)?"true":"false",
+ (ae->flags & HEALTH_ENTRY_FLAG_EXEC_FAILED)?"true":"false",
ae->exec?ae->exec:health.health_default_exec,
ae->recipient?ae->recipient:health.health_default_recipient,
ae->exec_code,
ALARM_ENTRY *t;
for(t = localhost.health_log.alarms ; t ; t = t->next) {
if(t->new_status != RRDCALC_STATUS_REMOVED)
- t->notifications |= HEALTH_ENTRY_NOTIFICATIONS_UPDATED;
+ t->flags |= HEALTH_ENTRY_FLAG_UPDATED;
}
// reset all thresholds to all charts
}
static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) {
- ae->notifications |= HEALTH_ENTRY_NOTIFICATIONS_PROCESSED;
+ ae->flags |= HEALTH_ENTRY_FLAG_PROCESSED;
+
+ if(unlikely(ae->new_status < RRDCALC_STATUS_CLEAR)) {
+ // do not send notifications for internal statuses
+ goto done;
+ }
// find the previous notification for the same alarm
+ // which we have run the exec script
ALARM_ENTRY *t;
for(t = ae->next; t ;t = t->next) {
- if(t->alarm_id == ae->alarm_id && t->notifications & HEALTH_ENTRY_NOTIFICATIONS_EXEC_RUN)
+ if(t->alarm_id == ae->alarm_id && t->flags & HEALTH_ENTRY_FLAG_EXEC_RUN)
break;
}
- if(t && t->new_status == ae->new_status) {
- // don't send the same notification again
- info("Health not sending again notification for alarm '%s.%s' status %s", ae->chart, ae->name, rrdcalc_status2string(ae->new_status));
- goto done;
+ if(likely(t)) {
+ // we have executed this alarm notification in the past
+ if (t && t->new_status == ae->new_status) {
+ // don't send the same notification again
+ debug(D_HEALTH, "Health not sending again notification for alarm '%s.%s' status %s", ae->chart, ae->name,
+ rrdcalc_status2string(ae->new_status));
+ goto done;
+ }
}
-
- if((ae->old_status == RRDCALC_STATUS_UNDEFINED && ae->new_status == RRDCALC_STATUS_UNINITIALIZED)
- || (ae->old_status == RRDCALC_STATUS_UNINITIALIZED && ae->new_status == RRDCALC_STATUS_CLEAR)) {
- info("Health not sending notification for first initialization of alarm '%s.%s' status %s", ae->chart, ae->name, rrdcalc_status2string(ae->new_status));
- goto done;
+ else {
+ // we have not executed this alarm notification in the past
+ if(unlikely(ae->old_status == RRDCALC_STATUS_UNINITIALIZED && ae->new_status == RRDCALC_STATUS_CLEAR)) {
+ debug(D_HEALTH, "Health not sending notification for first initialization of alarm '%s.%s' status %s", ae->chart, ae->name, rrdcalc_status2string(ae->new_status));
+ goto done;
+ }
}
char buffer[FILENAME_MAX + 1];
ae->info?ae->info:""
);
- ae->notifications |= HEALTH_ENTRY_NOTIFICATIONS_EXEC_RUN;
+ ae->flags |= HEALTH_ENTRY_FLAG_EXEC_RUN;
ae->exec_run_timestamp = time(NULL);
debug(D_HEALTH, "executing command '%s'", buffer);
debug(D_HEALTH, "done executing command - returned with code %d", ae->exec_code);
if(ae->exec_code != 0)
- ae->notifications |= HEALTH_ENTRY_NOTIFICATIONS_EXEC_FAILED;
+ ae->flags |= HEALTH_ENTRY_FLAG_EXEC_FAILED;
done:
health_alarm_log_save(host, ae);
}
static inline void health_process_notifications(RRDHOST *host, ALARM_ENTRY *ae) {
- info("Health alarm '%s.%s' = %0.2Lf - changed status from %s to %s",
+ debug(D_HEALTH, "Health alarm '%s.%s' = %0.2Lf - changed status from %s to %s",
ae->chart?ae->chart:"NOCHART", ae->name,
ae->new_value,
rrdcalc_status2string(ae->old_status),
ALARM_ENTRY *ae;
for(ae = host->health_log.alarms; ae && ae->unique_id >= stop_at_id ; ae = ae->next) {
if(unlikely(
- !(ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_PROCESSED) &&
- !(ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_UPDATED)
+ !(ae->flags & HEALTH_ENTRY_FLAG_PROCESSED) &&
+ !(ae->flags & HEALTH_ENTRY_FLAG_UPDATED)
)) {
if(unlikely(ae->unique_id < first_waiting))
#define RRDCALCTEMPLATE_HAS_CALCULATION(rt) ((rt)->after)
-#define HEALTH_ENTRY_NOTIFICATIONS_PROCESSED 0x00000001
-#define HEALTH_ENTRY_NOTIFICATIONS_UPDATED 0x00000002
-#define HEALTH_ENTRY_NOTIFICATIONS_EXEC_RUN 0x00000004
-#define HEALTH_ENTRY_NOTIFICATIONS_EXEC_FAILED 0x00000008
+#define HEALTH_ENTRY_FLAG_PROCESSED 0x00000001
+#define HEALTH_ENTRY_FLAG_UPDATED 0x00000002
+#define HEALTH_ENTRY_FLAG_EXEC_RUN 0x00000004
+#define HEALTH_ENTRY_FLAG_EXEC_FAILED 0x00000008
+#define HEALTH_ENTRY_FLAG_SAVED 0x10000000
typedef struct alarm_entry {
uint32_t unique_id;
int old_status;
int new_status;
- uint32_t notifications;
+ uint32_t flags;
int delay;
time_t delay_up_to_timestamp;
void log_date(FILE *out)
{
- char outstr[24];
+ char outstr[26];
time_t t;
struct tm *tmp, tmbuf;
tmp = localtime_r(&t, &tmbuf);
if (tmp == NULL) return;
- if (unlikely(strftime(outstr, sizeof(outstr), "%y-%m-%d %H:%M:%S", tmp) == 0)) return;
+ if (unlikely(strftime(outstr, sizeof(outstr), "%Y-%m-%d %H:%M:%S", tmp) == 0)) return;
fprintf(out, "%s: ", outstr);
}
log_date(stdout);
va_start( args, fmt );
- printf("DEBUG (%04lu@%-10.10s:%-15.15s): %s: ", line, file, function, program_name);
+ printf("%s: DEBUG (%04lu@%-10.10s:%-15.15s): ", program_name, line, file, function);
vprintf(fmt, args);
va_end( args );
putchar('\n');
log_date(stderr);
va_start( args, fmt );
- if(debug_flags) fprintf(stderr, "INFO (%04lu@%-10.10s:%-15.15s): %s: ", line, file, function, program_name);
- else fprintf(stderr, "INFO: %s: ", program_name);
+ if(debug_flags) fprintf(stderr, "%s: INFO: (%04lu@%-10.10s:%-15.15s):", program_name, line, file, function);
+ else fprintf(stderr, "%s: INFO: ", program_name);
vfprintf( stderr, fmt, args );
va_end( args );
log_date(stderr);
va_start( args, fmt );
- if(debug_flags) fprintf(stderr, "%s (%04lu@%-10.10s:%-15.15s): %s: ", prefix, line, file, function, program_name);
- else fprintf(stderr, "%s: %s: ", prefix, program_name);
+ if(debug_flags) fprintf(stderr, "%s: %s: (%04lu@%-10.10s:%-15.15s): ", program_name, prefix, line, file, function);
+ else fprintf(stderr, "%s: %s: ", program_name, prefix);
vfprintf( stderr, fmt, args );
va_end( args );
log_date(stderr);
va_start( args, fmt );
- if(debug_flags) fprintf(stderr, "FATAL (%04lu@%-10.10s:%-15.15s): %s: ", line, file, function, program_name);
- else fprintf(stderr, "FATAL: %s: ", program_name);
+ if(debug_flags) fprintf(stderr, "%s: FATAL: (%04lu@%-10.10s:%-15.15s): ", program_name, line, file, function);
+ else fprintf(stderr, "%s: FATAL: ", program_name);
vfprintf( stderr, fmt, args );
va_end( args );
#define D_REGISTRY 0x00200000
#define D_VARIABLES 0x00400000
#define D_HEALTH 0x00800000
+#define D_SYSTEM 0x80000000
//#define DEBUG (D_WEB_CLIENT_ACCESS|D_LISTENER|D_RRD_STATS)
//#define DEBUG 0xffffffff
error_log_limit_unlimited();
- info("Called: netdata_cleanup_and_exit()");
+ debug(D_EXIT, "Called: netdata_cleanup_and_exit()");
#ifdef NETDATA_INTERNAL_CHECKS
rrdset_free_all();
#else
}
if(tc_child_pid) {
- info("Killing tc-qos-helper procees");
+ debug(D_EXIT, "Killing tc-qos-helper procees");
if(killpid(tc_child_pid, SIGTERM) != -1)
waitid(P_PID, (id_t) tc_child_pid, &info, WEXITED);
}
if(debug_flags != 0) {
struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
if(setrlimit(RLIMIT_CORE, &rl) != 0)
- info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
+ error("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
+
prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
}
rrd_default_history_entries = (int) config_get_number("global", "history", RRD_DEFAULT_HISTORY_ENTRIES);
if(rrd_default_history_entries < 5 || rrd_default_history_entries > RRD_HISTORY_ENTRIES_MAX) {
- info("Invalid save lines %d given. Defaulting to %d.", rrd_default_history_entries, RRD_DEFAULT_HISTORY_ENTRIES);
+ error("Invalid history entries %d given. Defaulting to %d.", rrd_default_history_entries, RRD_DEFAULT_HISTORY_ENTRIES);
rrd_default_history_entries = RRD_DEFAULT_HISTORY_ENTRIES;
}
else {
rrd_update_every = (int) config_get_number("global", "update every", UPDATE_EVERY);
if(rrd_update_every < 1 || rrd_update_every > 600) {
- info("Invalid update timer %d given. Defaulting to %d.", rrd_update_every, UPDATE_EVERY_MAX);
+ error("Invalid data collection frequency (update every) %d given. Defaulting to %d.", rrd_update_every, UPDATE_EVERY_MAX);
rrd_update_every = UPDATE_EVERY;
}
else debug(D_OPTIONS, "update timer set to %d.", rrd_update_every);
if(debug_flags != 0) {
struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
if(setrlimit(RLIMIT_CORE, &rl) != 0)
- info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
+ error("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
}
#endif /* NETDATA_INTERNAL_CHECKS */
if(i != 0)
fatal("pthread_attr_setstacksize() to %zu bytes, failed with code %d.", wanted_stacksize, i);
else
- info("Successfully set pthread stacksize to %zu bytes", wanted_stacksize);
+ debug(D_SYSTEM, "Successfully set pthread stacksize to %zu bytes", wanted_stacksize);
}
// ------------------------------------------------------------------------
if(st->enabled) {
st->thread = mallocz(sizeof(pthread_t));
- info("Starting thread %s.", st->name);
+ debug(D_SYSTEM, "Starting thread %s.", st->name);
if(pthread_create(st->thread, &attr, st->start_routine, NULL))
error("failed to create new thread for %s.", st->name);
else if(pthread_detach(*st->thread))
error("Cannot request detach of newly created %s thread.", st->name);
}
- else info("Not starting thread %s.", st->name);
+ else debug(D_SYSTEM, "Not starting thread %s.", st->name);
}
// ------------------------------------------------------------------------
while(1) {
pause();
if(netdata_exit) {
- info("Exit main loop of netdata.");
+ debug(D_EXIT, "Exit main loop of netdata.");
netdata_cleanup_and_exit(0);
exit(0);
}
error("pre-execution of command '%s' on pid %d: failed to set default signal handler for SIGUSR2.", command, getpid());
}
- info("executing command: '%s' on pid %d.", command, getpid());
+ debug(D_CHILDS, "executing command: '%s' on pid %d.", command, getpid());
execl("/bin/sh", "sh", "-c", command, NULL);
exit(1);
}
#define DISK_TYPE_PARTITION 2
#define DISK_TYPE_CONTAINER 3
+#ifndef NETDATA_RELOAD_MOUNTINFO_EVERY
+#define NETDATA_RELOAD_MOUNTINFO_EVERY 10
+#endif
+
static struct disk {
char *disk; // the name of the disk (sda, sdb, etc)
unsigned long major;
unsigned long minor;
int sector_size;
int type;
+
char *mount_point;
+ uint32_t mount_point_hash;
// disk options caching
int configured;
static struct mountinfo *disk_mountinfo_root = NULL;
+static inline void mountinfo_reload(int force) {
+ static time_t last_loaded = 0;
+ time_t now = time(NULL);
+
+ if(force || now - last_loaded >= NETDATA_RELOAD_MOUNTINFO_EVERY) {
+//#ifdef NETDATA_INTERNAL_CHECKS
+// info("Reloading mountinfo");
+//#endif
+
+ // mountinfo_free() can be called with NULL disk_mountinfo_root
+ mountinfo_free(disk_mountinfo_root);
+
+ // re-read mountinfo in case something changed
+ disk_mountinfo_root = mountinfo_read();
+
+ last_loaded = now;
+ }
+}
+
+static inline void do_disk_space_stats(struct disk *d, const char *mount_point, const char *mount_source, const char *disk, const char *family, int update_every, unsigned long long dt) {
+ int do_space, do_inodes;
+
+ if(d) {
+ // verify we collected the metrics for the right disk.
+ // if not the mountpoint has changed.
+
+ struct stat buff_stat;
+ if(stat(mount_point, &buff_stat) == -1) {
+ error("Failed to stat() for '%s' (disk '%s')", mount_point, disk);
+ return;
+ }
+ else if(major(buff_stat.st_dev) != d->major || minor(buff_stat.st_dev) != d->minor) {
+ error("Disk '%s' (disk '%s') switched major:minor", mount_point, disk);
+ freez(d->mount_point);
+ d->mount_point = NULL;
+ d->mount_point_hash = 0;
+ return;
+ }
+
+ do_space = d->do_space;
+ do_inodes = d->do_inodes;
+ }
+ else {
+ char var_name[4096 + 1];
+ snprintfz(var_name, 4096, "plugin:proc:/proc/diskstats:%s", mount_point);
+
+ int def_space = CONFIG_ONDEMAND_ONDEMAND;
+
+ if(unlikely(strncmp(mount_point, "/run/user/", 10) == 0))
+ def_space = CONFIG_ONDEMAND_NO;
+
+ // check the user configuration (this will also show our 'on demand' decision)
+ def_space = config_get_boolean_ondemand(var_name, "enable space metrics", def_space);
+
+ int ddo_space = def_space,
+ ddo_inodes = def_space;
+
+ do_space = config_get_boolean_ondemand(var_name, "space usage", ddo_space);
+ do_inodes = config_get_boolean_ondemand(var_name, "inodes usage", ddo_inodes);
+ }
+
+ if(do_space == CONFIG_ONDEMAND_NO && do_inodes == CONFIG_ONDEMAND_NO)
+ return;
+
+ struct statvfs buff_statvfs;
+ if (statvfs(mount_point, &buff_statvfs) < 0) {
+ error("Failed statvfs() for '%s' (disk '%s')", mount_point, disk);
+ return;
+ }
+
+ // taken from get_fs_usage() found in coreutils
+ unsigned long bsize = (buff_statvfs.f_frsize) ? buff_statvfs.f_frsize : buff_statvfs.f_bsize;
+
+ fsblkcnt_t bavail = buff_statvfs.f_bavail;
+ fsblkcnt_t btotal = buff_statvfs.f_blocks;
+ fsblkcnt_t bavail_root = buff_statvfs.f_bfree;
+ fsblkcnt_t breserved_root = bavail_root - bavail;
+ fsblkcnt_t bused;
+ if(likely(btotal >= bavail_root))
+ bused = btotal - bavail_root;
+ else
+ bused = bavail_root - btotal;
+
+#ifdef NETDATA_INTERNAL_CHECKS
+ if(unlikely(btotal != bavail + breserved_root + bused))
+ error("Disk block statistics for '%s' (disk '%s') do not sum up: total = %llu, available = %llu, reserved = %llu, used = %llu", mount_point, disk, (unsigned long long)btotal, (unsigned long long)bavail, (unsigned long long)breserved_root, (unsigned long long)bused);
+#endif
+
+ // --------------------------------------------------------------------------
+
+ fsfilcnt_t favail = buff_statvfs.f_favail;
+ fsfilcnt_t ftotal = buff_statvfs.f_files;
+ fsfilcnt_t favail_root = buff_statvfs.f_ffree;
+ fsfilcnt_t freserved_root = favail_root - favail;
+ fsfilcnt_t fused = ftotal - favail_root;
+
+#ifdef NETDATA_INTERNAL_CHECKS
+ if(unlikely(btotal != bavail + breserved_root + bused))
+ error("Disk inode statistics for '%s' (disk '%s') do not sum up: total = %llu, available = %llu, reserved = %llu, used = %llu", mount_point, disk, (unsigned long long)ftotal, (unsigned long long)favail, (unsigned long long)freserved_root, (unsigned long long)fused);
+#endif
+
+ // --------------------------------------------------------------------------
+
+ RRDSET *st;
+
+ if(do_space == CONFIG_ONDEMAND_YES || (do_space == CONFIG_ONDEMAND_ONDEMAND && (bavail || breserved_root || bused))) {
+ st = rrdset_find_bytype("disk_space", disk);
+ if(!st) {
+ char title[4096 + 1];
+ snprintfz(title, 4096, "Disk Space Usage for %s [%s]", family, mount_source);
+ st = rrdset_create("disk_space", disk, NULL, family, "disk.space", title, "GB", 2023, update_every, RRDSET_TYPE_STACKED);
+
+ rrddim_add(st, "avail", NULL, bsize, 1024*1024*1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "used" , NULL, bsize, 1024*1024*1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "reserved_for_root", "reserved for root", bsize, 1024*1024*1024, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next_usec(st, dt);
+
+ rrddim_set(st, "avail", bavail);
+ rrddim_set(st, "used", bused);
+ rrddim_set(st, "reserved_for_root", breserved_root);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------------
+
+ if(do_inodes == CONFIG_ONDEMAND_YES || (do_inodes == CONFIG_ONDEMAND_ONDEMAND && (favail || freserved_root || fused))) {
+ st = rrdset_find_bytype("disk_inodes", disk);
+ if(!st) {
+ char title[4096 + 1];
+ snprintfz(title, 4096, "Disk Files (inodes) Usage for %s [%s]", family, mount_source);
+ st = rrdset_create("disk_inodes", disk, NULL, family, "disk.inodes", title, "Inodes", 2024, update_every, RRDSET_TYPE_STACKED);
+
+ rrddim_add(st, "avail", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "used" , NULL, 1, 1, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next_usec(st, dt);
+
+ rrddim_set(st, "avail", favail);
+ rrddim_set(st, "used", fused);
+ rrddim_set(st, "reserved_for_root", freserved_root);
+ rrdset_done(st);
+ }
+}
+
static struct disk *get_disk(unsigned long major, unsigned long minor, char *disk) {
static char path_to_get_hw_sector_size[FILENAME_MAX + 1] = "";
static char path_to_get_hw_sector_size_partitions[FILENAME_MAX + 1] = "";
// mountinfo_find() can be called with NULL disk_mountinfo_root
struct mountinfo *mi = mountinfo_find(disk_mountinfo_root, d->major, d->minor);
- if(unlikely(!mi)) {
- // mountinfo_free() can be called with NULL disk_mountinfo_root
- mountinfo_free(disk_mountinfo_root);
-
- // re-read mountinfo in case something changed
- disk_mountinfo_root = mountinfo_read();
+/* if(unlikely(!mi)) {
+ mountinfo_reload(1);
// search again for this disk
mi = mountinfo_find(disk_mountinfo_root, d->major, d->minor);
}
-
- if(mi)
+*/
+ if(mi) {
d->mount_point = strdupz(mi->mount_point);
- // no need to check for NULL
- else
+ d->mount_point_hash = mi->mount_point_hash;
+ }
+ else {
d->mount_point = NULL;
+ d->mount_point_hash = 0;
+ }
// ------------------------------------------------------------------------
// find the disk sector size
int do_proc_diskstats(int update_every, unsigned long long dt) {
static procfile *ff = NULL;
- static struct statvfs buff_statvfs;
- static struct stat buff_stat;
static int global_enable_new_disks_detected_at_runtime = CONFIG_ONDEMAND_YES,
global_enable_performance_for_physical_disks = CONFIG_ONDEMAND_ONDEMAND,
global_enable_performance_for_virtual_disks = CONFIG_ONDEMAND_NO,
uint32_t lines = procfile_lines(ff), l;
uint32_t words;
+ // this is smart enough not to reload it every time
+ mountinfo_reload(0);
+
for(l = 0; l < lines ;l++) {
// --------------------------------------------------------------------------
// Read parameters
char *disk;
- unsigned long long major = 0, minor = 0,
- reads = 0, mreads = 0, readsectors = 0, readms = 0,
+ unsigned long major = 0, minor = 0;
+
+ collected_number reads = 0, mreads = 0, readsectors = 0, readms = 0,
writes = 0, mwrites = 0, writesectors = 0, writems = 0,
- queued_ios = 0, busy_ms = 0, backlog_ms = 0,
- space_avail = 0, space_avail_root = 0, space_used = 0,
- inodes_avail = 0, inodes_avail_root = 0, inodes_used = 0;
+ queued_ios = 0, busy_ms = 0, backlog_ms = 0;
- unsigned long long last_reads = 0, last_readsectors = 0, last_readms = 0,
+ collected_number last_reads = 0, last_readsectors = 0, last_readms = 0,
last_writes = 0, last_writesectors = 0, last_writems = 0,
last_busy_ms = 0;
words = procfile_linewords(ff, l);
if(words < 14) continue;
- major = strtoull(procfile_lineword(ff, l, 0), NULL, 10);
- minor = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+ major = strtoul(procfile_lineword(ff, l, 0), NULL, 10);
+ minor = strtoul(procfile_lineword(ff, l, 1), NULL, 10);
disk = procfile_lineword(ff, l, 2);
// # of reads completed # of writes completed
if(d->mount_point)
def_performance = select_positive_option(def_performance, global_enable_performance_for_virtual_mountpoints);
-
break;
}
}
}
+/*
// --------------------------------------------------------------------------
// space metrics
if(d->mount_point && (d->do_space || d->do_inodes) ) {
- // collect space metrics using statvfs
+ do_disk_space_stats(d, d->mount_point, disk, disk, family, update_every, dt);
+ }
+*/
+ }
- if (statvfs(d->mount_point, &buff_statvfs) < 0)
- error("Failed statvfs() for '%s' (disk '%s')", d->mount_point, d->disk);
- else {
- space_avail = buff_statvfs.f_bavail * buff_statvfs.f_bsize;
- space_avail_root = (buff_statvfs.f_bfree - buff_statvfs.f_bavail) * buff_statvfs.f_bsize;
- space_used = (buff_statvfs.f_blocks - buff_statvfs.f_bfree) * buff_statvfs.f_bsize;
+ // --------------------------------------------------------------------------
+ // space metrics for non-block devices
+
+ struct mountinfo *mi;
+ for(mi = disk_mountinfo_root; mi ;mi = mi->next) {
+ if(unlikely(mi->flags & (MOUNTINFO_IS_DUMMY|MOUNTINFO_IS_BIND|MOUNTINFO_IS_SAME_DEV|MOUNTINFO_NO_STAT|MOUNTINFO_NO_SIZE)))
+ continue;
+
+/*
+ // skip the ones with block devices
+ int skip = 0;
+ struct disk *d;
+ for(d = disk_root; d ;d = d->next) {
+ if(unlikely(d->mount_point && mi->mount_point_hash == d->mount_point_hash && strcmp(mi->mount_point, d->mount_point))) {
+ skip = 1;
+ break;
+ }
+ }
- inodes_avail = buff_statvfs.f_favail;
- inodes_avail_root = buff_statvfs.f_ffree - buff_statvfs.f_favail;
- inodes_used = buff_statvfs.f_files - buff_statvfs.f_ffree;
+ if(unlikely(skip))
+ continue;
- // verify we collected the metrics for the right disk.
- // if not the mountpoint has changed.
+ // fprintf(stderr, "Will process mount point '%s', source '%s', filesystem '%s'\n", mi->mount_point, mi->mount_source, mi->filesystem);
+*/
- if(stat(d->mount_point, &buff_stat) == -1)
- error("Failed to stat() for '%s' (disk '%s')", d->mount_point, d->disk);
- else {
- if(major(buff_stat.st_dev) == major && minor(buff_stat.st_dev) == minor) {
-
- // --------------------------------------------------------------------------
-
- if(d->do_space == CONFIG_ONDEMAND_YES || (d->do_space == CONFIG_ONDEMAND_ONDEMAND && (space_avail || space_avail_root || space_used))) {
- st = rrdset_find_bytype("disk_space", disk);
- if(!st) {
- st = rrdset_create("disk_space", disk, NULL, family, "disk.space", "Disk Space Usage", "GB", 2023, update_every, RRDSET_TYPE_STACKED);
- st->isdetail = 1;
-
- rrddim_add(st, "avail", NULL, 1, 1024*1024*1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "used" , NULL, 1, 1024*1024*1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1024*1024*1024, RRDDIM_ABSOLUTE);
- }
- else rrdset_next_usec(st, dt);
-
- rrddim_set(st, "avail", space_avail);
- rrddim_set(st, "used", space_used);
- rrddim_set(st, "reserved_for_root", space_avail_root);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------------
-
- if(d->do_inodes == CONFIG_ONDEMAND_YES || (d->do_inodes == CONFIG_ONDEMAND_ONDEMAND && (inodes_avail || inodes_avail_root || inodes_used))) {
- st = rrdset_find_bytype("disk_inodes", disk);
- if(!st) {
- st = rrdset_create("disk_inodes", disk, NULL, family, "disk.inodes", "Disk Inodes Usage", "Inodes", 2024, update_every, RRDSET_TYPE_STACKED);
- st->isdetail = 1;
-
- rrddim_add(st, "avail", NULL, 1, 1, RRDDIM_ABSOLUTE);
- rrddim_add(st, "used" , NULL, 1, 1, RRDDIM_ABSOLUTE);
- rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1, RRDDIM_ABSOLUTE);
- }
- else rrdset_next_usec(st, dt);
-
- rrddim_set(st, "avail", inodes_avail);
- rrddim_set(st, "used", inodes_used);
- rrddim_set(st, "reserved_for_root", inodes_avail_root);
- rrdset_done(st);
- }
- }
- }
- }
- }
+ do_disk_space_stats(NULL, mi->mount_point, mi->mount_source, mi->persistent_id, mi->mount_point , update_every, dt);
}
return 0;
#include "common.h"
+// ----------------------------------------------------------------------------
+// taken from gnulib/mountlist.c
+
+#ifndef ME_REMOTE
+/* A file system is "remote" if its Fs_name contains a ':'
+ or if (it is of type (smbfs or cifs) and its Fs_name starts with '//')
+ or Fs_name is equal to "-hosts" (used by autofs to mount remote fs). */
+# define ME_REMOTE(Fs_name, Fs_type) \
+ (strchr (Fs_name, ':') != NULL \
+ || ((Fs_name)[0] == '/' \
+ && (Fs_name)[1] == '/' \
+ && (strcmp (Fs_type, "smbfs") == 0 \
+ || strcmp (Fs_type, "cifs") == 0)) \
+ || (strcmp("-hosts", Fs_name) == 0))
+#endif
+
+#define ME_DUMMY_0(Fs_name, Fs_type) \
+ (strcmp (Fs_type, "autofs") == 0 \
+ || strcmp (Fs_type, "proc") == 0 \
+ || strcmp (Fs_type, "subfs") == 0 \
+ /* for Linux 2.6/3.x */ \
+ || strcmp (Fs_type, "debugfs") == 0 \
+ || strcmp (Fs_type, "devpts") == 0 \
+ || strcmp (Fs_type, "fusectl") == 0 \
+ || strcmp (Fs_type, "mqueue") == 0 \
+ || strcmp (Fs_type, "rpc_pipefs") == 0 \
+ || strcmp (Fs_type, "sysfs") == 0 \
+ /* FreeBSD, Linux 2.4 */ \
+ || strcmp (Fs_type, "devfs") == 0 \
+ /* for NetBSD 3.0 */ \
+ || strcmp (Fs_type, "kernfs") == 0 \
+ /* for Irix 6.5 */ \
+ || strcmp (Fs_type, "ignore") == 0)
+
+/* Historically, we have marked as "dummy" any file system of type "none",
+ but now that programs like du need to know about bind-mounted directories,
+ we grant an exception to any with "bind" in its list of mount options.
+ I.e., those are *not* dummy entries. */
+# define ME_DUMMY(Fs_name, Fs_type) \
+ (ME_DUMMY_0 (Fs_name, Fs_type) || strcmp (Fs_type, "none") == 0)
+
+// ----------------------------------------------------------------------------
+
// find the mount info with the given major:minor
// in the supplied linked list of mountinfo structures
struct mountinfo *mountinfo_find(struct mountinfo *root, unsigned long major, unsigned long minor) {
freez(mi->root);
freez(mi->mount_point);
freez(mi->mount_options);
+ freez(mi->persistent_id);
/*
if(mi->optional_fields_count) {
mi = mallocz(sizeof(struct mountinfo));
- if(unlikely(!root))
- root = last = mi;
- else
- last->next = mi;
-
- last = mi;
- mi->next = NULL;
-
unsigned long w = 0;
mi->id = strtoul(procfile_lineword(ff, l, w), NULL, 10); w++;
mi->parentid = strtoul(procfile_lineword(ff, l, w), NULL, 10); w++;
if(!*minor) {
error("Cannot parse major:minor on '%s' at line %lu of '%s'", major, l + 1, filename);
+ freez(mi);
continue;
}
*minor = '\0';
minor++;
+ mi->flags = 0;
+
mi->major = strtoul(major, NULL, 10);
mi->minor = strtoul(minor, NULL, 10);
mi->mount_point = strdupz_decoding_octal(procfile_lineword(ff, l, w)); w++;
mi->mount_point_hash = simple_hash(mi->mount_point);
+ mi->persistent_id = strdupz(mi->mount_point);
+ netdata_fix_chart_id(mi->persistent_id);
+ mi->persistent_id_hash = simple_hash(mi->persistent_id);
+
mi->mount_options = strdupz(procfile_lineword(ff, l, w)); w++;
// count the optional fields
mi->mount_source_hash = simple_hash(mi->mount_source);
mi->super_options = strdupz(procfile_lineword(ff, l, w)); w++;
+
+ if(ME_DUMMY(mi->mount_source, mi->filesystem))
+ mi->flags |= MOUNTINFO_IS_DUMMY;
+
+ if(ME_REMOTE(mi->mount_source, mi->filesystem))
+ mi->flags |= MOUNTINFO_IS_REMOTE;
+
+ // mark as BIND the duplicates (i.e. same filesystem + same source)
+ {
+ struct stat buf;
+ if(unlikely(stat(mi->mount_point, &buf) == -1)) {
+ mi->st_dev = 0;
+ mi->flags |= MOUNTINFO_NO_STAT;
+ }
+ else {
+ mi->st_dev = buf.st_dev;
+
+ struct mountinfo *mt;
+ for(mt = root; mt; mt = mt->next) {
+ if(unlikely(mt->st_dev == mi->st_dev && !(mi->flags & MOUNTINFO_NO_STAT))) {
+ if(strlen(mi->mount_point) < strlen(mt->mount_point))
+ mt->flags |= MOUNTINFO_IS_SAME_DEV;
+ else
+ mi->flags |= MOUNTINFO_IS_SAME_DEV;
+ }
+ }
+ }
+ }
}
else {
mi->filesystem = NULL;
+ mi->filesystem_hash = 0;
+
mi->mount_source = NULL;
+ mi->mount_source_hash = 0;
+
mi->super_options = NULL;
+
+ mi->st_dev = 0;
}
+ // check if it has size
+ {
+ struct statvfs buff_statvfs;
+ if(statvfs(mi->mount_point, &buff_statvfs) < 0) {
+ mi->flags |= MOUNTINFO_NO_STAT;
+ }
+ else if(!buff_statvfs.f_blocks /* || !buff_statvfs.f_files */) {
+ mi->flags |= MOUNTINFO_NO_SIZE;
+ }
+ }
+
+ // link it
+ if(unlikely(!root))
+ root = mi;
+ else
+ last->next = mi;
+
+ last = mi;
+ mi->next = NULL;
+
/*
- info("MOUNTINFO: %u %u %u:%u root '%s', mount point '%s', mount options '%s', filesystem '%s', mount source '%s', super options '%s'",
+#ifdef NETDATA_INTERNAL_CHECKS
+ fprintf(stderr, "MOUNTINFO: %ld %ld %lu:%lu root '%s', persistent id '%s', mount point '%s', mount options '%s', filesystem '%s', mount source '%s', super options '%s'%s%s%s%s%s%s\n",
mi->id,
mi->parentid,
mi->major,
mi->minor,
mi->root,
- mi->mount_point,
- mi->mount_options,
- mi->filesystem,
- mi->mount_source,
- mi->super_options
+ mi->persistent_id,
+ (mi->mount_point)?mi->mount_point:"",
+ (mi->mount_options)?mi->mount_options:"",
+ (mi->filesystem)?mi->filesystem:"",
+ (mi->mount_source)?mi->mount_source:"",
+ (mi->super_options)?mi->super_options:"",
+ (mi->flags & MOUNTINFO_IS_DUMMY)?" DUMMY":"",
+ (mi->flags & MOUNTINFO_IS_BIND)?" BIND":"",
+ (mi->flags & MOUNTINFO_IS_REMOTE)?" REMOTE":"",
+ (mi->flags & MOUNTINFO_NO_STAT)?" NOSTAT":"",
+ (mi->flags & MOUNTINFO_NO_SIZE)?" NOSIZE":"",
+ (mi->flags & MOUNTINFO_IS_SAME_DEV)?" SAMEDEV":""
);
+#endif
*/
}
+/* find if the mount options have "bind" in them
+ {
+ FILE *fp = setmntent(MOUNTED, "r");
+ if (fp != NULL) {
+ struct mntent mntbuf;
+ struct mntent *mnt;
+ char buf[4096 + 1];
+
+ while ((mnt = getmntent_r(fp, &mntbuf, buf, 4096))) {
+ char *bind = hasmntopt(mnt, "bind");
+ if(bind) {
+ struct mountinfo *mi;
+ for(mi = root; mi ; mi = mi->next) {
+ if(strcmp(mnt->mnt_dir, mi->mount_point) == 0) {
+ fprintf(stderr, "Mount point '%s' is BIND\n", mi->mount_point);
+ mi->flags |= MOUNTINFO_IS_BIND;
+ break;
+ }
+ }
+
+#ifdef NETDATA_INTERNAL_CHECKS
+ if(!mi) {
+ error("Mount point '%s' not found in /proc/self/mountinfo", mnt->mnt_dir);
+ }
+#endif
+ }
+ }
+ endmntent(fp);
+ }
+ }
+*/
+
procfile_close(ff);
return root;
}
#ifndef NETDATA_PROC_SELF_MOUNTINFO_H
#define NETDATA_PROC_SELF_MOUNTINFO_H 1
+#define MOUNTINFO_IS_DUMMY 0x00000001
+#define MOUNTINFO_IS_REMOTE 0x00000002
+#define MOUNTINFO_IS_BIND 0x00000004
+#define MOUNTINFO_IS_SAME_DEV 0x00000008
+#define MOUNTINFO_NO_STAT 0x00000010
+#define MOUNTINFO_NO_SIZE 0x00000020
+
struct mountinfo {
long id; // mount ID: unique identifier of the mount (may be reused after umount(2)).
long parentid; // parent ID: ID of parent mount (or of self for the top of the mount tree).
unsigned long major; // major:minor: value of st_dev for files on filesystem (see stat(2)).
unsigned long minor;
+ char *persistent_id; // a calculated persistent id for the mount point
+ uint32_t persistent_id_hash;
+
char *root; // root: root of the mount within the filesystem.
uint32_t root_hash;
char *super_options; // super options: per-superblock options.
+ uint32_t flags;
+
+ dev_t st_dev; // id of device as given by stat()
+
struct mountinfo *next;
};
}
int registry_log_load(void) {
- char *s, buf[4096 + 1];
- size_t line = -1;
+ ssize_t line = -1;
// closing the log is required here
// otherwise we will append to it the values we read
if(!fp)
error("Registry: cannot open registry file: %s", registry.log_filename);
else {
+ char *s, buf[4096 + 1];
line = 0;
size_t len = 0;
+
while ((s = fgets_trim_len(buf, 4096, fp, &len))) {
line++;
// verify it is valid
if (unlikely(len < 85 || s[1] != '\t' || s[10] != '\t' || s[47] != '\t' || s[84] != '\t')) {
- error("Registry: log line %zu is wrong (len = %zu).", line, len);
+ error("Registry: log line %zd is wrong (len = %zu).", line, len);
continue;
}
s[1] = s[10] = s[47] = s[84] = '\0';
char *url = name;
while(*url && *url != '\t') url++;
if(!*url) {
- error("Registry: log line %zu does not have a url.", line);
+ error("Registry: log line %zd does not have a url.", line);
continue;
}
*url++ = '\0';
break;
default:
- error("Registry: ignoring line %zu of filename '%s': %s.", line, registry.log_filename, s);
+ error("Registry: ignoring line %zd of filename '%s': %s.", line, registry.log_filename, s);
break;
}
}
// rename the db to .old
debug(D_REGISTRY, "Registry: Link current db '%s' to .old: '%s'", registry.db_filename, old_filename);
if(link(registry.db_filename, old_filename) == -1 && errno != ENOENT)
- error("Registry: cannot move file '%s' to '%s'. Saving registry DB failed!", tmp_filename, registry.db_filename);
+ error("Registry: cannot move file '%s' to '%s'. Saving registry DB failed!", registry.db_filename, old_filename);
else {
// remove the database (it is saved in .old)
freez(r);
}
-inline void rrdr_done(RRDR *r)
+static inline void rrdr_done(RRDR *r)
{
r->rows = r->c + 1;
r->c = 0;
struct web_client *web_clients = NULL;
unsigned long long web_clients_count = 0;
-inline int web_client_crock_socket(struct web_client *w) {
+static inline int web_client_crock_socket(struct web_client *w) {
#ifdef TCP_CORK
if(likely(!w->tcp_cork && w->ofd != -1)) {
w->tcp_cork = 1;
return 0;
}
-inline int web_client_uncrock_socket(struct web_client *w) {
+static inline int web_client_uncrock_socket(struct web_client *w) {
#ifdef TCP_CORK
if(likely(w->tcp_cork && w->ofd != -1)) {
w->tcp_cork = 0;
\r
// ------------------------------------------------------------------------\r
// RETROSHARE\r
+\r
'retroshare.bandwidth': {\r
- info: 'Shows inbound and outbound traffic.',\r
+ info: 'RetroShare inbound and outbound traffic.',\r
mainheads: [\r
netdataDashboard.gaugeChart('Received', '12%', 'bandwidth_down_kb'),\r
netdataDashboard.gaugeChart('Sent', '12%', 'bandwidth_up_kb')\r
},\r
\r
'retroshare.peers': {\r
- info: 'Shows the number of (connected) friends.',\r
+ info: 'Number of (connected) RetroShare friends.',\r
mainheads: [\r
function(id) {\r
return '<div data-netdata="' + id + '"'\r
},\r
\r
'retroshare.dht': {\r
- info: 'Shows statistics about RetroShare\'s DHT. These values are estimated!'\r
+ info: 'Statistics about RetroShare\'s DHT. These values are estimated!'\r
+ },\r
+\r
+ // ------------------------------------------------------------------------\r
+ // fping\r
+\r
+ 'fping.loss': {\r
+ colors: NETDATA.colors[1],\r
+ height: 0.5\r
+ },\r
+\r
+ 'fping.packets': {\r
+ height: 0.5\r
}\r
+\r
};\r
});
NETDATA.requiredJs.push({
- url: NETDATA.serverDefault + 'dashboard_info.js?v20161002-1',
+ url: NETDATA.serverDefault + 'dashboard_info.js?v20161016-1',
async: false,
isAlreadyLoaded: function() { return false; }
});
"info": {
"title": "NetData API",
"description": "Real time data collection and graphs...",
- "version": "1.2.1_master"
+ "version": "1.4.1_master"
},
"host": "registry.my-netdata.io",
"schemes": [
{
"name": "after",
"in": "query",
- "description": "This parameter can either be an absolute timestamp specifying the starting point of the data to be returned, or a relative number of seconds, to the last collected timestamp. Netdata will assume it is a relative number if it is smaller than the duration of the round robin database for this chart. So, if the round robin database is 3600 seconds, any value from -3600 to 3600 will trigger relative arithmetics. Netdata will adapt this parameter to the boundaries of the round robin database.",
+ "description": "This parameter can either be an absolute timestamp specifying the starting point of the data to be returned, or a relative number of seconds (negative, relative to parameter: before). Netdata will assume it is a relative number if it is less that 3 years (in seconds). Netdata will adapt this parameter to the boundaries of the round robin database. The default is the beginning of the round robin database (i.e. by default netdata will attempt to return data for the entire database).",
"required": true,
"type": "number",
"format": "integer",
{
"name": "before",
"in": "query",
- "description": "This parameter can either be an absolute timestamp specifying the ending point of the data to be returned, or a relative number of seconds, to the last collected timestamp. Netdata will assume it is a relative number if it is smaller than the duration of the round robin database for this chart. So, if the round robin database is 3600 seconds, any value from -3600 to 3600 will trigger relative arithmetics. Netdata will adapt this parameter to the boundaries of the round robin database.",
+ "description": "This parameter can either be an absolute timestamp specifying the ending point of the data to be returned, or a relative number of seconds (negative), relative to the last collected timestamp. Netdata will assume it is a relative number if it is less than 3 years (in seconds). Netdata will adapt this parameter to the boundaries of the round robin database. The default is zero (i.e. the timestamp of the last value collected).",
"required": false,
"type": "number",
"format": "integer",
{
"name": "points",
"in": "query",
- "description": "The number of points to be returned. If not given, or it is <= 0, or it is bigger than the points stored in the round robin database for this chart for the given duration, all the available collected values for the given duration are returned.",
+ "description": "The number of points to be returned. If not given, or it is <= 0, or it is bigger than the points stored in the round robin database for this chart for the given duration, all the available collected values for the given duration will be returned.",
"required": true,
"type": "number",
"format": "integer",
{
"name": "group",
"in": "query",
- "description": "The grouping method. If multiple collected values are to be grouped in order to return fewer points, this parameters defines the method of grouping. Two methods are supported, \"max\" and \"average\". \"max\" is actually calculated on the absolute value collected (so it works for both positive and negative dimesions to return the most extreme value in either direction).",
+ "description": "The grouping method. If multiple collected values are to be grouped in order to return fewer points, this parameters defines the method of grouping. methods supported \"min\", \"max\", \"average\", \"sum\", \"incremental-sum\". \"max\" is actually calculated on the absolute value collected (so it works for both positive and negative dimesions to return the most extreme value in either direction).",
"required": true,
"type": "string",
"enum": [
+ "min",
"max",
- "average"
+ "average",
+ "sum",
+ "incremental-sum"
],
"default": "average",
"allowEmptyValue": false
"null2zero",
"objectrows",
"google_json",
- "percentage"
+ "percentage",
+ "unaligned"
],
"collectionFormat": "pipes"
},
"allowEmptyValue": false,
"default": "system.cpu"
},
+ {
+ "name": "alarm",
+ "in": "query",
+ "description": "the name of an alarm linked to the chart",
+ "required": false,
+ "type": "string",
+ "format": "any text",
+ "allowEmptyValue": true
+ },
{
"name": "dimension",
"in": "query",
"format": "integer",
"default": 0
},
- {
- "name": "points",
- "in": "query",
- "description": "The number of points to be returned. If not given, or it is <= 0, or it is bigger than the points stored in the round robin database for this chart for the given duration, all the available collected values for the given duration are returned.",
- "required": true,
- "type": "number",
- "format": "integer",
- "allowEmptyValue": false,
- "default": 20
- },
{
"name": "group",
"in": "query",
- "description": "The grouping method. If multiple collected values are to be grouped in order to return fewer points, this parameters defines the method of grouping. Two methods are supported, \"max\" and \"average\". \"max\" is actually calculated on the absolute value collected (so it works for both positive and negative dimesions to return the most extreme value in either direction).",
+ "description": "The grouping method. If multiple collected values are to be grouped in order to return fewer points, this parameters defines the method of grouping. methods are supported \"min\", \"max\", \"average\", \"sum\", \"incremental-sum\". \"max\" is actually calculated on the absolute value collected (so it works for both positive and negative dimesions to return the most extreme value in either direction).",
"required": true,
"type": "string",
"enum": [
+ "min",
"max",
- "average"
+ "average",
+ "sum",
+ "incremental-sum"
],
"default": "average",
"allowEmptyValue": false
"items": {
"type": "string",
"enum": [
- "nonzero",
- "flip",
"abs",
"absolute",
"absolute-sum",
"null2zero",
- "percentage"
+ "percentage",
+ "unaligned"
],
"collectionFormat": "pipes"
},
info:
title: NetData API
description: 'Real time data collection and graphs...'
- version: 1.2.1_master
+ version: 1.4.1_master
host: registry.my-netdata.io
schemes:
- http
allowEmptyValue: false
- name: after
in: query
- description: 'This parameter can either be an absolute timestamp specifying the starting point of the data to be returned, or a relative number of seconds (relative to parameter: before). Netdata will assume it is a relative number if it is smaller than the duration of the round robin database for this chart. So, if the round robin database is 3600 seconds, any value from -3600 to 3600 will trigger relative arithmetics. Netdata will adapt this parameter to the boundaries of the round robin database.'
+ description: 'This parameter can either be an absolute timestamp specifying the starting point of the data to be returned, or a relative number of seconds (negative, relative to parameter: before). Netdata will assume it is a relative number if it is less that 3 years (in seconds). Netdata will adapt this parameter to the boundaries of the round robin database. The default is the beginning of the round robin database (i.e. by default netdata will attempt to return data for the entire database).'
required: true
type: number
format: integer
default: -600
- name: before
in: query
- description: 'This parameter can either be an absolute timestamp specifying the ending point of the data to be returned, or a relative number of seconds, to the last collected timestamp. Netdata will assume it is a relative number if it is smaller than the duration of the round robin database for this chart. So, if the round robin database is 3600 seconds, any value from -3600 to 3600 will trigger relative arithmetics. Netdata will adapt this parameter to the boundaries of the round robin database.'
+ description: 'This parameter can either be an absolute timestamp specifying the ending point of the data to be returned, or a relative number of seconds (negative), relative to the last collected timestamp. Netdata will assume it is a relative number if it is less than 3 years (in seconds). Netdata will adapt this parameter to the boundaries of the round robin database. The default is zero (i.e. the timestamp of the last value collected).'
required: false
type: number
format: integer
default: 0
- name: points
in: query
- description: 'The number of points to be returned. If not given, or it is <= 0, or it is bigger than the points stored in the round robin database for this chart for the given duration, all the available collected values for the given duration are returned.'
+ description: 'The number of points to be returned. If not given, or it is <= 0, or it is bigger than the points stored in the round robin database for this chart for the given duration, all the available collected values for the given duration will be returned.'
required: true
type: number
format: integer