]> arthur.barton.de Git - netdata.git/commitdiff
Merge pull request #1070 from tperalta82/master
authorCosta Tsaousis <costa@tsaousis.gr>
Sun, 23 Oct 2016 19:21:08 +0000 (22:21 +0300)
committerGitHub <noreply@github.com>
Sun, 23 Oct 2016 19:21:08 +0000 (22:21 +0300)
Added pushbullet notifications support

85 files changed:
.gitignore
CMakeLists.txt
LICENSE.md
Makefile.am
README.md
ansible/netdata.yml [deleted file]
charts.d/ap.chart.sh
charts.d/apache.chart.sh
charts.d/apcupsd.chart.sh
charts.d/cpu_apps.chart.sh
charts.d/cpufreq.chart.sh
charts.d/example.chart.sh
charts.d/exim.chart.sh
charts.d/hddtemp.chart.sh
charts.d/load_average.chart.sh
charts.d/mem_apps.chart.sh
charts.d/mysql.chart.sh
charts.d/nginx.chart.sh
charts.d/nut.chart.sh
charts.d/opensips.chart.sh
charts.d/phpfpm.chart.sh
charts.d/postfix.chart.sh
charts.d/sensors.chart.sh
charts.d/squid.chart.sh
charts.d/tomcat.chart.sh
conf.d/Makefile.am
conf.d/fping.conf [new file with mode: 0644]
conf.d/health.d/softnet.conf
conf.d/health_alarm_notify.conf
conf.d/node.d.conf [new file with mode: 0644]
conf.d/node.d/README.md [new file with mode: 0644]
conf.d/node.d/named.conf.md [new file with mode: 0644]
conf.d/node.d/sma_webbox.conf.md [new file with mode: 0644]
conf.d/node.d/snmp.conf.md [new file with mode: 0644]
conf.d/python.d/redis.conf
configs.signatures
contrib/README.md
contrib/debian/netdata.postinst.in
diagrams/build.sh [new file with mode: 0755]
diagrams/config.puml [new file with mode: 0644]
diagrams/registry.puml [new file with mode: 0644]
netdata-installer.sh
netdata.spec.in
node.d/named.node.js
node.d/node_modules/netdata.js
node.d/sma_webbox.node.js
node.d/snmp.node.js
plugins.d/Makefile.am
plugins.d/alarm-notify.sh
plugins.d/cgroup-name.sh
plugins.d/charts.d.plugin
plugins.d/fping.plugin [new file with mode: 0755]
plugins.d/node.d.plugin
plugins.d/tc-qos-helper.sh
python.d/README.md
python.d/dovecot.chart.py
python.d/python_modules/base.py
python.d/python_modules/msg.py
src/common.c
src/common.h
src/daemon.c
src/health.c
src/health.h
src/log.c
src/log.h
src/main.c
src/popen.c
src/proc_diskstats.c
src/proc_self_mountinfo.c
src/proc_self_mountinfo.h
src/registry.c
src/rrd2json.c
src/web_client.c
web/dashboard.html
web/dashboard.js
web/dashboard_info.js
web/demo.html
web/demo2.html
web/demosites.html
web/goto-host-from-alarm.html
web/index.html
web/netdata-swagger.json
web/netdata-swagger.yaml
web/registry.html
web/tv.html

index c281ea32468481f134d53bf3c6fc4ad93946930a..30962eaa59f0337cfb97cbe369fd8788702526d4 100644 (file)
@@ -89,3 +89,10 @@ profile/benchmark-dictionary
 profile/benchmark-registry
 
 *.pyc
+
+diagrams/*.png
+diagrams/*.svg
+diagrams/*.atxt
+diagrams/plantuml.jar
+
+netdata.cppcheck
index e973f6ebec54cf96bb4f868b88dc5bff92f5606c..9c4aa760729de421d1a1887ff4a66ffbd94b8a9b 100755 (executable)
@@ -20,8 +20,12 @@ set(NETDATA_SOURCE_FILES
         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
@@ -53,9 +57,9 @@ set(NETDATA_SOURCE_FILES
         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
@@ -70,8 +74,8 @@ set(NETDATA_SOURCE_FILES
         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
@@ -84,7 +88,7 @@ set(NETDATA_SOURCE_FILES
         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
index f0742900047edfa9fabcfeb2ab76c418ba4b70ff..564b084e062270819565f1ebdd8a939a44d0e7ce 100644 (file)
@@ -55,22 +55,28 @@ connectivity is not available.
     [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/)
@@ -142,14 +148,3 @@ connectivity is not available.
     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)
-
-
index ea1ac256bbbbb8fb5e3cff33868c43ef8d848aec..8b4bc8376906902079a6c5484be743c92fea2ef9 100644 (file)
@@ -39,6 +39,8 @@ SUBDIRS = \
        $(NULL)
 
 dist_noinst_DATA= \
+       diagrams/config.puml \
+       diagrams/registry.puml \
        configs.signatures \
        Dockerfile \
        netdata.spec \
@@ -47,7 +49,7 @@ dist_noinst_DATA= \
 # 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 \
index 7f16a342c392f3014e86e72648083e3adbd7b158..3eae8d8a57df92972663e69c130fb8e516fc5945 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,15 +1,24 @@
 # netdata [![Build Status](https://travis-ci.org/firehol/netdata.svg?branch=master)](https://travis-ci.org/firehol/netdata) [![Coverity Scan Build Status](https://scan.coverity.com/projects/9140/badge.svg)](https://scan.coverity.com/projects/firehol-netdata) [![Docker Pulls](https://img.shields.io/docker/pulls/titpetric/netdata.svg)](https://hub.docker.com/r/titpetric/netdata/)
 
-> New to netdata? Live demo: [http://my-netdata.io](http://my-netdata.io)
+> *New to netdata? Here is a live demo: [http://my-netdata.io](http://my-netdata.io)*
 
-**netdata** is a highly optimized Linux daemon providing **real-time performance and health monitoring for Linux systems, applications and SNMP devices, over the web**! It has been designed to permanently run on all systems, without disrupting the applications running on them.
+**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 **fast** and **efficient**, designed to permanently run on all systems
+(**physical** & **virtual** servers, **containers**, **IoT** devices), without
+disrupting their core function._
 
 ---
 
-**netdata** user and installation base, as reported by the [global public netdata registry](https://github.com/firehol/netdata/wiki/mynetdata-menu-item):
+## User base
 
+*Since May 16th 2016 (the date the [global public netdata registry](https://github.com/firehol/netdata/wiki/mynetdata-menu-item) was released):*<br/>
 [![User Base](https://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_entries&dimensions=persons&label=user%20base&units=null&value_color=blue&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) [![Monitored Servers](https://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_entries&dimensions=machines&label=servers%20monitored&units=null&value_color=orange&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) [![Sessions Served](https://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_sessions&label=sessions%20served&units=null&value_color=yellowgreen&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry)
 
+*in the last 24 hours:*<br/>
 [![New Users Today](http://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_entries&dimensions=persons&after=-86400&options=unaligned&group=incremental-sum&label=new%20users%20today&units=null&value_color=blue&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) [![New Machines Today](https://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_entries&dimensions=machines&group=incremental-sum&after=-86400&options=unaligned&label=servers%20added%20today&units=null&value_color=orange&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) [![Sessions Today](https://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_sessions&after=-86400&group=incremental-sum&options=unaligned&label=sessions%20served%20today&units=null&value_color=yellowgreen&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry)
 
 ---
 <a href="https://octoverse.github.com/" target="_blank"><img src="https://cloud.githubusercontent.com/assets/2662304/18795170/ec321f32-81cb-11e6-92a8-d03492f0b00d.png"/></a>
 </p>
 
-`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
-> - improved plugins: mysql, cgroups, hddtemp, sensors, phpfm, tc (QoS)
+`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
+ - improved plugins: mysql, cgroups, hddtemp, sensors, phpfm, tc (QoS)
 
 ---
 
-## At a glance
+## Features
 
 <p align="center">
 <img src="https://cloud.githubusercontent.com/assets/2662304/19168687/f6a567be-8c19-11e6-8561-ce8d589e8346.gif"/>
 </p>
 
-**netdata** visualizes the **truth of now** in its **greatest detail**, so that you can get insights of what is happening now and what just happened, on your systems and applications:
-
- - real-time, per second updates, snappy refreshes!
- - 300+ charts out of the box, 2000+ metrics monitored!
- - zero configuration, zero maintenance, zero dependencies!
- - dozens of health monitoring alarms, out of the box!
+ - **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)
 
 ---
 
-## Features
-
-This is what you get:
+## What does it monitor?
 
-- **Stunning bootstrap dashboards**, out of the box (theme-able: dark, light)
-- **Blazingly fast** and **super efficient**, mostly written in C (for default installations, expect just 2% of a single core CPU usage and a few MB of RAM)
-- **Zero configuration** - you just install it and it auto-detects everything
-- **Zero dependencies**, it is even its own web server for its static web files and its web API
-- **Zero maintenance**, you just run it, it does the rest
-- **Custom dashboards** that can be built using simple HTML (no javascript necessary)
-- **Extensible**, you can monitor anything you can get a metric for, using its Plugin API (anything can be a netdata plugin - from BASH to python and node.js, so you can easily monitor any application, any API)
-- **Embeddable**, it can run anywhere a Linux kernel runs (even IoT) and its charts can be embedded on your web pages too
-- **Powerful alarms and notifications**, it can send alarms as email, slack.com messages, pushover.net mobile push notifications, telegram.org messages, and more.
+netdata monitors several thousands of metrics per device.
+All these metrics are collected and visualized in real-time.
 
----
-
-## What does it monitor?
+> _Almost all metrics are auto-detected, without any configuration._
 
-This is what it currently monitors (most with zero configuration):
+This is a list of what it currently monitors:
 
-- **CPU usage, interrupts, softirqs and frequency** (total and per core)
+- **CPU**<br/>
+  usage, interrupts, softirqs, frequency, total and per core
 
-- **RAM, swap and kernel memory usage** (including KSM and kernel memory deduper)
+- **Memory**<br/>
+  RAM, swap and kernel memory usage, including KSM the kernel memory deduper
 
-- **Disks** (per disk: I/O, operations, backlog, utilization, space, etc)
+- **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.
 
diff --git a/ansible/netdata.yml b/ansible/netdata.yml
deleted file mode 100644 (file)
index 4c7e736..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
----
-- 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
index 7b4f690bb47ece250d03c249e58f294560d3758f..0e85c486dc95daedb9404d680ac37f42f5a70dfc 100755 (executable)
@@ -1,5 +1,11 @@
 # 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=
@@ -7,13 +13,11 @@ ap_priority=6900
 
 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 = "";
@@ -43,6 +47,7 @@ ap_check() {
        #  - 1 to disable the chart
 
        [ ${#ap_devs[@]} -gt 0 ] && return 0
+       error "no devices found in AP mode, with 'iw dev'"
        return 1
 }
 
index 2d68d43b277fecae2b1356a4554ea8291522902f..b503e74e3d2cdb8e8acd25e937c23d19aea858e9 100755 (executable)
@@ -1,5 +1,11 @@
 # 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=
@@ -67,14 +73,14 @@ apache_detect() {
 
        # 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}" \
@@ -92,7 +98,7 @@ apache_detect() {
 
 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}"
 
@@ -130,7 +136,7 @@ apache_get() {
                -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
 
@@ -151,7 +157,7 @@ apache_check() {
        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
 
index df18aaa2e83535b86c35c59fed3c8a7a5b6034fa..24905afe912ac178c428eaa0d1a799e8427b481d 100755 (executable)
@@ -1,5 +1,11 @@
 # 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
 
@@ -12,7 +18,7 @@ apcupsd_timeout=3
 apcupsd_priority=90000
 
 apcupsd_get() {
-       timeout $apcupsd_timeout apcaccess status "$1:$2"
+       run -t $apcupsd_timeout apcaccess status "$1:$2"
 }
 
 apcupsd_check() {
@@ -23,14 +29,14 @@ 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
 
@@ -146,7 +152,7 @@ END {
        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
 }
index 6b2513dcf04f9c0a8f1a9961d891196248305bf3..8e075831a5b51a9f6485ed9a410663914a9b2cef 100755 (executable)
@@ -1,5 +1,10 @@
 # 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
 
@@ -19,7 +24,7 @@ cpu_apps_check() {
 
        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
index 06f692fa691a78308309534f208cd6e001be49b7..b358ac3e74827455d95ef5753bd1b0735c27e9fa 100755 (executable)
@@ -1,5 +1,11 @@
 # 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_
 
@@ -51,7 +57,7 @@ cpufreq_create() {
 
                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 )"
@@ -59,7 +65,6 @@ cpufreq_create() {
        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
index 93f1cf4fddbbc6a7cc22f904b1b47f79d9c4007b..86fde490134e077d0d84e9bcbe40ffe818659623 100755 (executable)
@@ -1,5 +1,11 @@
 # 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_
 
@@ -67,7 +73,7 @@ example_check() {
        #  - 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
@@ -108,7 +114,6 @@ BEGIN example.random2 $1
 SET random = $example_value4
 END
 VALUESEOF
-       # echo >&2 "example_count = $example_count value = $value4"
 
        return 0
 }
index c60ae9460dff2636ffe893a238e4075001268aca..4c70f2c196328295a99c86bb7c0e0d4b995ef3f1 100644 (file)
@@ -1,5 +1,13 @@
 # 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
@@ -8,28 +16,15 @@ exim_update_every=5
 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
 
@@ -37,16 +32,16 @@ exim_check() {
 }
 
 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
 }
index 41c3e2478092a7df949fe8cb0c8ca353e0abe00b..15895c5e160a71a845a586ab1f602492b26b3061 100755 (executable)
@@ -1,5 +1,12 @@
 # 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"
@@ -13,7 +20,8 @@ hddtemp_priority=90000
 
 # _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
index e6790d8077672ec6331cca81104401fcf47e5999..70d3aec7a2dd2c086295f699780be9ae80f347fb 100755 (executable)
@@ -1,5 +1,11 @@
 # 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
 
index ab95b361c88d178e0035006aa8fbf486261d72e1..3bc65fe24004c9af4e71ebcd88b764f38e5b16eb 100755 (executable)
@@ -1,5 +1,11 @@
 # 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
@@ -15,7 +21,7 @@ mem_apps_check() {
 
        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
index 120fec66e32586d45e70847b0ff8444088d1a71f..86feac997c8ac260d3f8ebc9bf23c5fd7d11e4ba 100755 (executable)
@@ -1,5 +1,11 @@
 # 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
@@ -17,9 +23,9 @@ mysql_get() {
        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
@@ -74,13 +80,13 @@ mysql_check() {
                [ -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]
@@ -97,7 +103,7 @@ mysql_check() {
                        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
@@ -318,7 +324,7 @@ mysql_update() {
                        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
 
@@ -510,7 +516,7 @@ VALUESEOF
                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
 }
 
index a2a9b320fa2766d560a503d31c28f4820b90e986..0ae7d630775af82d3c4580ba0c6fc6cf401ba15e 100755 (executable)
@@ -1,5 +1,11 @@
 # 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_
 
@@ -20,7 +26,7 @@ nginx_reading=0
 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" \
@@ -34,7 +40,7 @@ nginx_get() {
                 -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
 
@@ -55,7 +61,7 @@ nginx_get() {
                -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
 
@@ -68,7 +74,7 @@ nginx_check() {
        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
 
index 3c8e1c9d0548be6a3058932185f97049a5e290dd..e0b1b4cf981bedbb6e0a8de8c59cecb414d1c77d 100755 (executable)
@@ -1,5 +1,11 @@
 # 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=
@@ -15,11 +21,11 @@ nut_priority=90000
 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() {
@@ -42,12 +48,12 @@ 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
 
@@ -179,9 +185,9 @@ END {
        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
 }
index 779087e353e1bca7aef79da3b340c89a0ebafe0c..2a0249daed4d048082188e325c3894b81111f7d0 100755 (executable)
@@ -1,5 +1,11 @@
 # 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
@@ -7,7 +13,7 @@ opensips_timeout=2
 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" \
@@ -31,7 +37,7 @@ opensips_check() {
        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
 
@@ -214,7 +220,7 @@ opensips_update() {
        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
index 976ce91b198d260ebf8fa8ebf709abae6e636bb9..a5ee0ad561637dd3dd30571e966d1863c019ffb4 100755 (executable)
@@ -1,9 +1,13 @@
 # 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/
 
@@ -32,7 +36,7 @@ phpfpm_slow_requests=0
 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:" \
@@ -46,7 +50,7 @@ phpfpm_get() {
                || "${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
 
@@ -83,7 +87,7 @@ phpfpm_get() {
                || -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
 
@@ -101,14 +105,14 @@ phpfpm_check() {
        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
        
index 7f07a18682c0f57c255bc9cc9c303befebbd0109..8106414a1f6da024482f348782bb97e2e5ec0abf 100755 (executable)
@@ -1,5 +1,11 @@
 # 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=
@@ -18,23 +24,11 @@ postfix_check() {
        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
 
@@ -73,10 +67,10 @@ postfix_update() {
        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
index 9652f896a0822f39cf65f7047fe5f05681daae99..125c925da7f6c031fcbf45bdc78a76cf552bd561 100755 (executable)
@@ -1,5 +1,11 @@
 # 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
 
@@ -43,7 +49,7 @@ sensors_check() {
        #  - 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
 }
 
@@ -64,7 +70,7 @@ sensors_check_files() {
                [ $v -ne 0 ] && echo "$f" && continue
                excluded=
 
-               echo >&2 "$PROGRAM_NAME: sensors: $f gives zero values"
+               error "$f gives zero values"
        done
 }
 
@@ -83,7 +89,7 @@ sensors_check_temp_type() {
                v=$(( v + 1 - 1 ))
                [ $v -ne 0 ] && echo "$f" && continue
 
-               echo >&2 "$PROGRAM_NAME: sensors: $f is disabled"
+               error "$f is disabled"
        done
 }
 
@@ -121,7 +127,7 @@ sensors_create() {
 
                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
@@ -221,7 +227,6 @@ sensors_create() {
        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
index 3e72ba6df507d7442b01a565bcb4f073db80f113..2c19c35d59068f11728cddc6433a786a5bed24fc 100755 (executable)
@@ -1,5 +1,11 @@
 # 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=
@@ -9,7 +15,7 @@ squid_priority=60000
 
 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() {
@@ -29,13 +35,13 @@ squid_autodetect() {
                                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
 }
 
@@ -53,7 +59,7 @@ squid_check() {
        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
 
index cc6baea1ae1f967fe2210086d96ffb13ed8611b9..44a92c86e7bf35f27fb9beb2c324ac056b08720b 100755 (executable)
@@ -1,5 +1,12 @@
 # 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
 
@@ -34,13 +41,13 @@ tomcat_check() {
 
        # 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}"
@@ -49,7 +56,7 @@ tomcat_check() {
        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}"
@@ -60,8 +67,7 @@ tomcat_check() {
        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
 
@@ -75,8 +81,8 @@ tomcat_check() {
 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 \
index 066744cab721b9c3dad5ab5c56fa0814971d8a24..c2ef258c9550f95118331ecb32f75742ffeb657a 100644 (file)
@@ -6,6 +6,8 @@ MAINTAINERCLEANFILES= $(srcdir)/Makefile.in
 dist_config_DATA = \
        apps_groups.conf \
        charts.d.conf \
+       fping.conf \
+       node.d.conf \
        python.d.conf \
        health_alarm_notify.conf \
        health_email_recipients.conf \
@@ -17,6 +19,10 @@ dist_chartsconfig_DATA = \
 
 nodeconfigdir=$(configdir)/node.d
 dist_nodeconfig_DATA = \
+       node.d/README.md \
+       node.d/named.conf.md \
+       node.d/sma_webbox.conf.md \
+       node.d/snmp.conf.md \
        $(NULL)
 
 pythonconfigdir=$(configdir)/python.d
diff --git a/conf.d/fping.conf b/conf.d/fping.conf
new file mode 100644 (file)
index 0000000..3bf8047
--- /dev/null
@@ -0,0 +1,32 @@
+# 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
index 0c3709f4609876c5442c186294aaa6e2064b2683..420a45448a2e766711baecf3d2b347d77713be12 100644 (file)
@@ -15,7 +15,7 @@
   lookup: sum -1h unaligned absolute of squeezed
    units: events
    every: 1m
-    warn: $this > 0
+    warn: $this > (($status >= $WARNING)  ? (0) : (10))
    delay: down 30m multiplier 1.5 max 1h
     info: number of times ksoftirq ran out of sysctl net.core.netdev_budget or time slice, with work remaining (this can be a cause for dropped packets)
       to: silent
index d7209ed77e8912b13174cf9731b38f018c022ff1..71589ccea57789632cb4f72998bf506d488532df 100644 (file)
@@ -142,7 +142,7 @@ DEFAULT_RECIPIENT_PUSHBULLET=""
 # Users also need to open a query with the bot (see below).
 
 # note: multiple recipients can be given like this:
-#                  "CHAT_ID_1 CHAT_ID_1 ..."
+#                  "CHAT_ID_1 CHAT_ID_2 ..."
 
 # enable/disable sending telegram messages
 SEND_TELEGRAM="YES"
diff --git a/conf.d/node.d.conf b/conf.d/node.d.conf
new file mode 100644 (file)
index 0000000..95aec99
--- /dev/null
@@ -0,0 +1,39 @@
+{\r
+       "___help_1": "Default options for node.d.plugin - this is a JSON file.",\r
+       "___help_2": "Use http://jsonlint.com/ to verify it is valid JSON.",\r
+       "___help_3": "------------------------------------------------------------",\r
+\r
+       "___help_update_every": "Minimum data collection frequency for all node.d/*.node.js modules. Set it to 0 to inherit it from netdata.",\r
+       "update_every": 0,\r
+\r
+       "___help_modules_enable_autodetect": "Enable/disable auto-detection for node.d/*.node.js modules that support it.",\r
+       "modules_enable_autodetect": true,\r
+\r
+       "___help_modules_enable_all": "Enable all node.d/*.node.js modules by default.",\r
+       "modules_enable_all": true,\r
+\r
+       "___help_modules": "Enable/disable the following modules. Give only XXX for node.d/XXX.node.js",\r
+       "modules": {\r
+               "named": {\r
+                       "enabled": true\r
+               },\r
+               "sma_webbox": {\r
+                       "enabled": true\r
+               },\r
+               "snmp": {\r
+                       "enabled": true\r
+               }\r
+       },\r
+\r
+       "___help_paths": "Paths that control the operation of node.d.plugin",\r
+       "paths": {\r
+               "___help_plugins": "The full path to the modules javascript node.d/ directory",\r
+               "plugins": null,\r
+\r
+               "___help_config": "The full path to the modules configs node.d/ directory",\r
+               "config": null,\r
+\r
+               "___help_modules": "Array of paths to add to node.js when searching for node_modules",\r
+               "modules": []\r
+       }\r
+}\r
diff --git a/conf.d/node.d/README.md b/conf.d/node.d/README.md
new file mode 100644 (file)
index 0000000..45e3d02
--- /dev/null
@@ -0,0 +1,7 @@
+`node.d.plugin` modules accept configuration in JSON format.
+
+Unfortunately, JSON files do not accept comments. So, the best way to describe them is to have markdown text files with instructions.
+
+JSON has a very strict formatting. If you get errors from netdata at `/var/log/netdata/error.log` that a certain configuration file cannot be loaded, we suggest to verify it at [http://jsonlint.com/](http://jsonlint.com/).
+
+The files in this directory, provide usable examples for configuring each `node.d.plugin` module.
diff --git a/conf.d/node.d/named.conf.md b/conf.d/node.d/named.conf.md
new file mode 100644 (file)
index 0000000..fa843dd
--- /dev/null
@@ -0,0 +1,344 @@
+# ISC Bind Statistics\r
+\r
+Using this netdata collector, you can monitor one or more ISC Bind servers.\r
+\r
+The source code for this plugin in [here](https://github.com/firehol/netdata/blob/master/node.d/named.node.js).\r
+\r
+## Example netdata charts\r
+\r
+Depending on the number of views your bind has, you may get a large number of charts.\r
+Here this is with just one view:\r
+\r
+![image](https://cloud.githubusercontent.com/assets/2662304/12765473/879b8e04-ca07-11e5-817d-b0651996c42b.png)\r
+![image](https://cloud.githubusercontent.com/assets/2662304/12766538/12b272fa-ca0d-11e5-81e1-6a9f8ff488ff.png)\r
+\r
+## How it works\r
+\r
+The plugin will execute (from within node.js) the equivalent of:\r
+\r
+```sh\r
+curl "http://localhost:8888/json/v1/server"\r
+```\r
+\r
+Here is a sample of the output this command produces.\r
+\r
+```js\r
+{\r
+  "json-stats-version":"1.0",\r
+  "boot-time":"2016-01-31T08:20:48Z",\r
+  "config-time":"2016-01-31T09:28:03Z",\r
+  "current-time":"2016-02-02T22:22:20Z",\r
+  "opcodes":{\r
+    "QUERY":247816,\r
+    "IQUERY":0,\r
+    "STATUS":0,\r
+    "RESERVED3":0,\r
+    "NOTIFY":0,\r
+    "UPDATE":3813,\r
+    "RESERVED6":0,\r
+    "RESERVED7":0,\r
+    "RESERVED8":0,\r
+    "RESERVED9":0,\r
+    "RESERVED10":0,\r
+    "RESERVED11":0,\r
+    "RESERVED12":0,\r
+    "RESERVED13":0,\r
+    "RESERVED14":0,\r
+    "RESERVED15":0\r
+  },\r
+  "qtypes":{\r
+    "A":89519,\r
+    "NS":863,\r
+    "CNAME":1,\r
+    "SOA":1,\r
+    "PTR":116779,\r
+    "MX":276,\r
+    "TXT":198,\r
+    "AAAA":39324,\r
+    "SRV":850,\r
+    "ANY":5\r
+  },\r
+  "nsstats":{\r
+    "Requestv4":251630,\r
+    "ReqEdns0":1255,\r
+    "ReqTSIG":3813,\r
+    "ReqTCP":57,\r
+    "AuthQryRej":1455,\r
+    "RecQryRej":122,\r
+    "Response":245918,\r
+    "TruncatedResp":44,\r
+    "RespEDNS0":1255,\r
+    "RespTSIG":3813,\r
+    "QrySuccess":205159,\r
+    "QryAuthAns":119495,\r
+    "QryNoauthAns":120770,\r
+    "QryNxrrset":32711,\r
+    "QrySERVFAIL":262,\r
+    "QryNXDOMAIN":2395,\r
+    "QryRecursion":40885,\r
+    "QryDuplicate":5712,\r
+    "QryFailure":1577,\r
+    "UpdateDone":2514,\r
+    "UpdateFail":1299,\r
+    "UpdateBadPrereq":1276,\r
+    "QryUDP":246194,\r
+    "QryTCP":45,\r
+    "OtherOpt":101\r
+  },\r
+  "views":{\r
+    "local":{\r
+      "resolver":{\r
+        "stats":{\r
+          "Queryv4":74577,\r
+          "Responsev4":67032,\r
+          "NXDOMAIN":601,\r
+          "SERVFAIL":5,\r
+          "FORMERR":7,\r
+          "EDNS0Fail":7,\r
+          "Truncated":3071,\r
+          "Lame":4,\r
+          "Retry":11826,\r
+          "QueryTimeout":1838,\r
+          "GlueFetchv4":6864,\r
+          "GlueFetchv4Fail":30,\r
+          "QryRTT10":112,\r
+          "QryRTT100":42900,\r
+          "QryRTT500":23275,\r
+          "QryRTT800":534,\r
+          "QryRTT1600":97,\r
+          "QryRTT1600+":20,\r
+          "BucketSize":31,\r
+          "REFUSED":13\r
+        },\r
+        "qtypes":{\r
+          "A":64931,\r
+          "NS":870,\r
+          "CNAME":185,\r
+          "PTR":5,\r
+          "MX":49,\r
+          "TXT":149,\r
+          "AAAA":7972,\r
+          "SRV":416\r
+        },\r
+        "cache":{\r
+          "A":40356,\r
+          "NS":8032,\r
+          "CNAME":14477,\r
+          "PTR":2,\r
+          "MX":21,\r
+          "TXT":32,\r
+          "AAAA":3301,\r
+          "SRV":94,\r
+          "DS":237,\r
+          "RRSIG":2301,\r
+          "NSEC":126,\r
+          "!A":52,\r
+          "!NS":4,\r
+          "!TXT":1,\r
+          "!AAAA":3797,\r
+          "!SRV":9,\r
+          "NXDOMAIN":590\r
+        },\r
+        "cachestats":{\r
+          "CacheHits":1085188,\r
+          "CacheMisses":109,\r
+          "QueryHits":464755,\r
+          "QueryMisses":55624,\r
+          "DeleteLRU":0,\r
+          "DeleteTTL":42615,\r
+          "CacheNodes":5188,\r
+          "CacheBuckets":2079,\r
+          "TreeMemTotal":2326026,\r
+          "TreeMemInUse":1508075,\r
+          "HeapMemMax":132096,\r
+          "HeapMemTotal":393216,\r
+          "HeapMemInUse":132096\r
+        },\r
+        "adb":{\r
+          "nentries":1021,\r
+          "entriescnt":3157,\r
+          "nnames":1021,\r
+          "namescnt":3022\r
+        }\r
+      }\r
+    },\r
+    "public":{\r
+      "resolver":{\r
+        "stats":{\r
+          "BucketSize":31\r
+        },\r
+        "qtypes":{\r
+        },\r
+        "cache":{\r
+        },\r
+        "cachestats":{\r
+          "CacheHits":0,\r
+          "CacheMisses":0,\r
+          "QueryHits":0,\r
+          "QueryMisses":0,\r
+          "DeleteLRU":0,\r
+          "DeleteTTL":0,\r
+          "CacheNodes":0,\r
+          "CacheBuckets":64,\r
+          "TreeMemTotal":287392,\r
+          "TreeMemInUse":29608,\r
+          "HeapMemMax":1024,\r
+          "HeapMemTotal":262144,\r
+          "HeapMemInUse":1024\r
+        },\r
+        "adb":{\r
+          "nentries":1021,\r
+          "nnames":1021\r
+        }\r
+      }\r
+    },\r
+    "_bind":{\r
+      "resolver":{\r
+        "stats":{\r
+          "BucketSize":31\r
+        },\r
+        "qtypes":{\r
+        },\r
+        "cache":{\r
+        },\r
+        "cachestats":{\r
+          "CacheHits":0,\r
+          "CacheMisses":0,\r
+          "QueryHits":0,\r
+          "QueryMisses":0,\r
+          "DeleteLRU":0,\r
+          "DeleteTTL":0,\r
+          "CacheNodes":0,\r
+          "CacheBuckets":64,\r
+          "TreeMemTotal":287392,\r
+          "TreeMemInUse":29608,\r
+          "HeapMemMax":1024,\r
+          "HeapMemTotal":262144,\r
+          "HeapMemInUse":1024\r
+        },\r
+        "adb":{\r
+          "nentries":1021,\r
+          "nnames":1021\r
+        }\r
+      }\r
+    }\r
+  }\r
+}\r
+```\r
+\r
+\r
+From this output it collects:\r
+\r
+- Global Received Requests by IP version (IPv4, IPv6)\r
+- Global Successful Queries\r
+- Current Recursive Clients\r
+- Global Queries by IP Protocol (TCP, UDP)\r
+- Global Queries Analysis\r
+- Global Received Updates\r
+- Global Query Failures\r
+- Global Query Failures Analysis\r
+- Other Global Server Statistics\r
+- Global Incoming Requests by OpCode\r
+- Global Incoming Requests by Query Type\r
+- Global Socket Statistics (will only work if the url is `http://127.0.0.1:8888/json/v1`, i.e. without `/server`, but keep in mind this produces a very long output and probably will account for 0.5% CPU overhead alone, per bind server added)\r
+- Per View Statistics (the following set will be added for each bind view):\r
+   - View, Resolver Active Queries\r
+   - View, Resolver Statistics\r
+   - View, Resolver Round Trip Timings\r
+   - View, Requests by Query Type\r
+\r
+## Configuration\r
+\r
+The collector (optionally) reads a configuration file named `/etc/netdata/node.d/named.conf`, with the following contents:\r
+\r
+```js\r
+{\r
+    "enable_autodetect": true,\r
+    "update_every": 5,\r
+    "servers": [\r
+        {\r
+            "name": "bind1",\r
+            "url": "http://127.0.0.1:8888/json/v1/server",\r
+            "update_every": 1\r
+        },\r
+        {\r
+            "name": "bind2",\r
+            "url": "http://10.1.2.3:8888/json/v1/server",\r
+            "update_every": 2\r
+        }\r
+    ]\r
+}\r
+```\r
+\r
+You can add any number of bind servers.\r
+\r
+If the configuration file is missing, or the key `enable_autodetect` is `true`, the collector will also attempt to fetch `http://localhost:8888/json/v1/server` which, if successful will be added too.\r
+\r
+### XML instead of JSON, from bind\r
+\r
+The collector can also accept bind URLs that return XML output. This might required if you cannot have bind 9.10+ with JSON but you have an version of bind that supports XML statistics v3. Check [this](https://www.isc.org/blogs/bind-9-10-statistics-troubleshooting-and-zone-configuration/) for versions supported.\r
+\r
+In such cases, use a URL like this:\r
+\r
+```sh\r
+curl "http://localhost:8888/xml/v3/server"\r
+```\r
+\r
+Only `xml` and `v3` has been tested.\r
+\r
+Keep in mind though, that XML parsing is done using javascript code, which requires a triple conversion:\r
+\r
+1. from XML to JSON using a javascript XML parser (**CPU intensive**),\r
+2. which is then transformed to emulate the output of the JSON output of bind (**CPU intensive** - and yes the converted JSON from XML is different to the native JSON - even bind produces different names for various attributes),\r
+3. which is then processed to generate the data for the charts (this will happen even if bind is producing JSON).\r
+\r
+In general, expect XML parsing to be 2 to 3 times more CPU intensive than JSON.\r
+\r
+**So, if you can use the JSON output of bind, prefer it over XML**. Keep also in mind that even bind will use more CPU when generating XML instead of JSON.\r
+\r
+The XML interface of bind is not autodetected.\r
+You will have to provide the config file `/etc/netdata/node.d/named.conf`, like this:\r
+\r
+```js\r
+{\r
+    "enable_autodetect": false,\r
+    "update_every": 1,\r
+    "servers": [\r
+        {\r
+            "name": "local",\r
+            "url": "http://localhost:8888/xml/v3/server",\r
+            "update_every": 1\r
+        }\r
+    ]\r
+}\r
+```\r
+\r
+Of course, you can monitor more than one bind servers. Each one can be configured with either JSON or XML output.\r
+\r
+## Auto-detection\r
+\r
+Auto-detection is controlled by `enable_autodetect` in the config file. The default is enabled, so that if the collector can connect to `http://localhost:8888/json/v1/server` to receive bind statistics, it will automatically enable it.\r
+\r
+## Bind (named) configuration\r
+\r
+To use this plugin, you have to have bind v9.10+ properly compiled to provide statistics in `JSON` format.\r
+\r
+For more information on how to get your bind installation ready, please refer to the [bind statistics channel developer comments](http://jpmens.net/2013/03/18/json-in-bind-9-s-statistics-server/) and to [bind documentation](https://ftp.isc.org/isc/bind/9.10.3/doc/arm/Bv9ARM.ch06.html#statistics) or [bind Knowledge Base article AA-01123](https://kb.isc.org/article/AA-01123/0).\r
+\r
+Normally, you will need something like this in your `named.conf`:\r
+\r
+```\r
+statistics-channels {\r
+        inet 127.0.0.1 port 8888 allow { 127.0.0.1; };\r
+        inet ::1 port 8888 allow { ::1; };\r
+};\r
+```\r
+\r
+(use the IPv4 or IPv6 line depending on what you are using, you can also use both)\r
+\r
+Verify it works by running the following command (the collector is written in node.js and will query your bind server directly, but if this command works, the collector should be able to work too):\r
+\r
+```sh\r
+curl "http://localhost:8888/json/v1/server"\r
+```\r
+\r
diff --git a/conf.d/node.d/sma_webbox.conf.md b/conf.d/node.d/sma_webbox.conf.md
new file mode 100644 (file)
index 0000000..19fdc9d
--- /dev/null
@@ -0,0 +1,25 @@
+\r
+[SMA Sunny Webbox](http://www.solar-is-future.com/sma-technology-for-our-future/products/sunny-webbox/index.html)\r
+\r
+Example netdata configuration for node.d/sma_webbox.conf\r
+\r
+The module supports any number of name servers, like this:\r
+\r
+```json\r
+{\r
+    "enable_autodetect": false,\r
+    "update_every": 5,\r
+    "servers": [\r
+        {\r
+            "name": "plant1",\r
+            "hostname": "10.0.1.1",\r
+            "update_every": 10\r
+        },\r
+        {\r
+            "name": "plant2",\r
+            "hostname": "10.0.2.1",\r
+            "update_every": 15\r
+        }\r
+    ]\r
+}\r
+```\r
diff --git a/conf.d/node.d/snmp.conf.md b/conf.d/node.d/snmp.conf.md
new file mode 100644 (file)
index 0000000..bae5bf2
--- /dev/null
@@ -0,0 +1,341 @@
+# SNMP Data Collector\r
+\r
+Using this collector, netdata can collect data from any SNMP device.\r
+\r
+This collector supports:\r
+\r
+- any number of SNMP devices\r
+- each SNMP device can be used to collect data for any number of charts\r
+- each chart may have any number of dimensions\r
+- each SNMP device may have a different update frequency\r
+- each SNMP device will accept one or more batches to report values (you can set `max_request_size` per SNMP server, to control the size of batches).\r
+\r
+The source code of the plugin is [here](https://github.com/firehol/netdata/blob/master/node.d/snmp.node.js).\r
+\r
+## Configuration\r
+\r
+You will need to create the file `/etc/netdata/node.d/snmp.conf` with data like the following.\r
+\r
+In this example:\r
+\r
+ - the SNMP device is `10.11.12.8`.\r
+ - the SNMP community is `public`.\r
+ - we will update the values every 10 seconds (`update_every: 10` under the server `10.11.12.8`).\r
+ - we define 2 charts `snmp_switch.bandwidth_port1` and `snmp_switch.bandwidth_port2`, each having 2 dimensions: `in` and `out`.\r
+\r
+```js\r
+{\r
+    "enable_autodetect": false,\r
+    "update_every": 5,\r
+    "max_request_size": 100,\r
+    "servers": [\r
+        {\r
+            "hostname": "10.11.12.8",\r
+            "community": "public",\r
+            "update_every": 10,\r
+            "max_request_size": 50,\r
+            "options": { "timeout": 10000 },\r
+            "charts": {\r
+                "snmp_switch.bandwidth_port1": {\r
+                    "title": "Switch Bandwidth for port 1",\r
+                    "units": "kilobits/s",\r
+                    "type": "area",\r
+                    "priority": 1,\r
+                    "family": "ports",\r
+                    "dimensions": {\r
+                        "in": {\r
+                            "oid": "1.3.6.1.2.1.2.2.1.10.1",\r
+                            "algorithm": "incremental",\r
+                            "multiplier": 8,\r
+                            "divisor": 1024\r
+                        },\r
+                        "out": {\r
+                            "oid": "1.3.6.1.2.1.2.2.1.16.1",\r
+                            "algorithm": "incremental",\r
+                            "multiplier": -8,\r
+                            "divisor": 1024\r
+                        }\r
+                    }\r
+                },\r
+                "snmp_switch.bandwidth_port2": {\r
+                    "title": "Switch Bandwidth for port 2",\r
+                    "units": "kilobits/s",\r
+                    "type": "area",\r
+                    "priority": 1,\r
+                    "family": "ports",\r
+                    "dimensions": {\r
+                        "in": {\r
+                            "oid": "1.3.6.1.2.1.2.2.1.10.2",\r
+                            "algorithm": "incremental",\r
+                            "multiplier": 8,\r
+                            "divisor": 1024\r
+                        },\r
+                        "out": {\r
+                            "oid": "1.3.6.1.2.1.2.2.1.16.2",\r
+                            "algorithm": "incremental",\r
+                            "multiplier": -8,\r
+                            "divisor": 1024\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+        }\r
+    ]\r
+}\r
+```\r
+\r
+`update_every` is the update frequency for each server, in seconds.\r
+\r
+`max_request_size` limits the maximum number of OIDs that will be requested in a single call. The default is 50. Lower this number of you get `TooBig` errors in netdata error.log.\r
+\r
+`family` sets the name of the submenu of the dashboard each chart will appear under.\r
+\r
+If you need to define many charts using incremental OIDs, you can use something like this:\r
+\r
+This is like the previous, but the option `multiply_range` given, will multiply the current chart from `1` to `24` inclusive, producing 24 charts in total for the 24 ports of the switch `10.11.12.8`.\r
+\r
+Each of the 24 new charts will have its id (1-24) appended at:\r
+\r
+1. its chart unique id, i.e. `snmp_switch.bandwidth_port1` to `snmp_switch.bandwidth_port24`\r
+2. its `title`, i.e. `Switch Bandwidth for port 1` to `Switch Bandwidth for port 24`\r
+3. its `oid` (for all dimensions), i.e. dimension `in` will be `1.3.6.1.2.1.2.2.1.10.1` to `1.3.6.1.2.1.2.2.1.10.24`\r
+3. its priority (which will be incremented for each chart so that the charts will appear on the dashboard in this order)\r
+\r
+```js\r
+{\r
+    "enable_autodetect": false,\r
+    "update_every": 10,\r
+    "servers": [\r
+        {\r
+            "hostname": "10.11.12.8",\r
+            "community": "public",\r
+            "update_every": 10,\r
+            "options": { "timeout": 20000 },\r
+            "charts": {\r
+                "snmp_switch.bandwidth_port": {\r
+                    "title": "Switch Bandwidth for port ",\r
+                    "units": "kilobits/s",\r
+                    "type": "area",\r
+                    "priority": 1,\r
+                    "family": "ports",\r
+                    "multiply_range": [ 1, 24 ],\r
+                    "dimensions": {\r
+                        "in": {\r
+                            "oid": "1.3.6.1.2.1.2.2.1.10.",\r
+                            "algorithm": "incremental",\r
+                            "multiplier": 8,\r
+                            "divisor": 1024\r
+                        },\r
+                        "out": {\r
+                            "oid": "1.3.6.1.2.1.2.2.1.16.",\r
+                            "algorithm": "incremental",\r
+                            "multiplier": -8,\r
+                            "divisor": 1024\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+        }\r
+    ]\r
+}\r
+```\r
+\r
+The `options` given for each server, are:\r
+\r
+ - `timeout`, the time to wait for the SNMP device to respond. The default is 5000 ms.\r
+ - `version`, the SNMP version to use. `0` is Version 1, `1` is Version 2c. The default is Version 1 (`0`).\r
+ - `transport`, the default is `udp4`.\r
+ - `port`, the port of the SNMP device to connect to. The default is `161`.\r
+ - `retries`, the number of attempts to make to fetch the data. The default is `1`.\r
+\r
+## Retreiving names from snmp\r
+\r
+You can append a value retrieved from SNMP to the title, by adding `titleoid` to the chart.\r
+\r
+You can set a dimension name to a value retrieved from SNMP, by adding `oidname` to the dimension.\r
+\r
+Both of the above will participate in `multiply_range`.\r
+\r
+\r
+## Testing the configuration\r
+\r
+To test it, you can run:\r
+\r
+```sh\r
+/usr/libexec/netdata/plugins.d/node.d.plugin 1 snmp\r
+```\r
+\r
+The above will run it on your console and you will be able to see what netdata sees, but also errors. You can get a very detailed output by appending `debug` to the command line.\r
+\r
+If it works, restart netdata to activate the snmp collector and refresh the dashboard (if your SNMP device responds with a delay, you may need to refresh the dashboard in a few seconds).\r
+\r
+## Data collection speed\r
+\r
+Keep in mind that many SNMP switches are routers are very slow. They may not be able to report values per second. If you run `node.d.plugin` in `debug` mode, it will report the time it took for the SNMP device to respond. My switch, for example, needs 7-8 seconds to respond for the traffic on 24 ports (48 OIDs, in/out).\r
+\r
+Also, if you use many SNMP clients on the same SNMP device at the same time, values may be skipped. This is a problem of the SNMP device, not this collector.\r
+\r
+## Finding OIDs\r
+\r
+Use `snmpwalk`, like this:\r
+\r
+```sh\r
+snmpwalk -t 20 -v 1 -O fn -c public 10.11.12.8\r
+```\r
+\r
+- `-t 20` is the timeout in seconds\r
+- `-v 1` is the SNMP version\r
+- `-O fn` will display full OIDs in numeric format (you may want to run it also without this option to see human readable output of OIDs)\r
+- `-c public` is the SNMP community\r
+- `10.11.12.8` is the SNMP device\r
+\r
+Keep in mind that `snmpwalk` outputs the OIDs with a dot in front them. You should remove this dot when adding OIDs to the configuration file of this collector.\r
+\r
+## Example: Linksys SRW2024P\r
+\r
+This is what I use for my Linksys SRW2024P. It creates:\r
+\r
+1. A chart for power consumption (it is a PoE switch)\r
+2. Two charts for packets received (total packets received and packets received with errors)\r
+3. One chart for packets output\r
+4. 24 charts, one for each port of the switch. It also appends the port names, as defined at the switch, to the chart titles.\r
+\r
+This switch also reports various other metrics, like snmp, packets per port, etc. Unfortunately it does not report CPU utilization or backplane utilization.\r
+\r
+This switch has a very slow SNMP processors. To respond, it needs about 8 seconds, so I have set the refresh frequency (`update_every`) to 15 seconds.\r
+\r
+```js\r
+{\r
+        "enable_autodetect": false,\r
+        "update_every": 5,\r
+        "servers": [\r
+                {\r
+                        "hostname": "10.11.12.8",\r
+                        "community": "public",\r
+                        "update_every": 15,\r
+                        "options": { "timeout": 20000, "version": 1 },\r
+                        "charts": {\r
+                                "snmp_switch.power": {\r
+                                        "title": "Switch Power Supply",\r
+                                        "units": "watts",\r
+                                        "type": "line",\r
+                                        "priority": 10,\r
+                                        "family": "power",\r
+                                        "dimensions": {\r
+                                                "supply": {\r
+                                                        "oid": ".1.3.6.1.2.1.105.1.3.1.1.2.1",\r
+                                                        "algorithm": "absolute",\r
+                                                        "multiplier": 1,\r
+                                                        "divisor": 1\r
+                                                },\r
+                                                "used": {\r
+                                                        "oid": ".1.3.6.1.2.1.105.1.3.1.1.4.1",\r
+                                                        "algorithm": "absolute",\r
+                                                        "multiplier": 1,\r
+                                                        "divisor": 1\r
+                                                }\r
+                                        }\r
+                                }\r
+                                , "snmp_switch.input": {\r
+                                        "title": "Switch Packets Input",\r
+                                        "units": "packets/s",\r
+                                        "type": "area",\r
+                                        "priority": 20,\r
+                                        "family": "IP",\r
+                                        "dimensions": {\r
+                                                "receives": {\r
+                                                        "oid": ".1.3.6.1.2.1.4.3.0",\r
+                                                        "algorithm": "incremental",\r
+                                                        "multiplier": 1,\r
+                                                        "divisor": 1\r
+                                                }\r
+                                                , "discards": {\r
+                                                        "oid": ".1.3.6.1.2.1.4.8.0",\r
+                                                        "algorithm": "incremental",\r
+                                                        "multiplier": 1,\r
+                                                        "divisor": 1\r
+                                                }\r
+                                        }\r
+                                }\r
+                                , "snmp_switch.input_errors": {\r
+                                        "title": "Switch Received Packets with Errors",\r
+                                        "units": "packets/s",\r
+                                        "type": "line",\r
+                                        "priority": 30,\r
+                                        "family": "IP",\r
+                                        "dimensions": {\r
+                                                "bad_header": {\r
+                                                        "oid": ".1.3.6.1.2.1.4.4.0",\r
+                                                        "algorithm": "incremental",\r
+                                                        "multiplier": 1,\r
+                                                        "divisor": 1\r
+                                                }\r
+                                                , "bad_address": {\r
+                                                        "oid": ".1.3.6.1.2.1.4.5.0",\r
+                                                        "algorithm": "incremental",\r
+                                                        "multiplier": 1,\r
+                                                        "divisor": 1\r
+                                                }\r
+                                                , "unknown_protocol": {\r
+                                                        "oid": ".1.3.6.1.2.1.4.7.0",\r
+                                                        "algorithm": "incremental",\r
+                                                        "multiplier": 1,\r
+                                                        "divisor": 1\r
+                                                }\r
+                                        }\r
+                                }\r
+                                , "snmp_switch.output": {\r
+                                        "title": "Switch Output Packets",\r
+                                        "units": "packets/s",\r
+                                        "type": "line",\r
+                                        "priority": 40,\r
+                                        "family": "IP",\r
+                                        "dimensions": {\r
+                                                "requests": {\r
+                                                        "oid": ".1.3.6.1.2.1.4.10.0",\r
+                                                        "algorithm": "incremental",\r
+                                                        "multiplier": 1,\r
+                                                        "divisor": 1\r
+                                                }\r
+                                                , "discards": {\r
+                                                        "oid": ".1.3.6.1.2.1.4.11.0",\r
+                                                        "algorithm": "incremental",\r
+                                                        "multiplier": -1,\r
+                                                        "divisor": 1\r
+                                                }\r
+                                                , "no_route": {\r
+                                                        "oid": ".1.3.6.1.2.1.4.12.0",\r
+                                                        "algorithm": "incremental",\r
+                                                        "multiplier": -1,\r
+                                                        "divisor": 1\r
+                                                }\r
+                                        }\r
+                                }\r
+                                , "snmp_switch.bandwidth_port": {\r
+                                        "title": "Switch Bandwidth for port ",\r
+                                        "titleoid": ".1.3.6.1.2.1.31.1.1.1.18.",\r
+                                        "units": "kilobits/s",\r
+                                        "type": "area",\r
+                                        "priority": 100,\r
+                                        "family": "ports",\r
+                                        "multiply_range": [ 1, 24 ],\r
+                                        "dimensions": {\r
+                                                "in": {\r
+                                                        "oid": ".1.3.6.1.2.1.2.2.1.10.",\r
+                                                        "algorithm": "incremental",\r
+                                                        "multiplier": 8,\r
+                                                        "divisor": 1024\r
+                                                }\r
+                                                , "out": {\r
+                                                        "oid": ".1.3.6.1.2.1.2.2.1.16.",\r
+                                                        "algorithm": "incremental",\r
+                                                        "multiplier": -8,\r
+                                                        "divisor": 1024\r
+                                                }\r
+                                        }\r
+                                }\r
+                        }\r
+                }\r
+        ]\r
+}\r
+```\r
index 9935bff77f6d366e9f8be7f07b3a998e48df0043..e1ce2de2e649a8e7ba36db2eedca45e548416341 100644 (file)
@@ -56,7 +56,7 @@
 #
 # Additionally to the above, redis also supports the following:
 #
-#     socket: 'path/to/mysql.sock'
+#     socket: 'path/to/redis.sock'
 #
 #  or
 #     host: 'IP or HOSTNAME' # the host to connect to
index 24603ae9fe1c43bce4ed044887909e8381537ff9..9f440e9262db6f19b1ece48db58e644ec588e7e1 100644 (file)
@@ -29,6 +29,20 @@ declare -A configs_signatures=(
   ['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'
@@ -131,6 +145,7 @@ declare -A configs_signatures=(
   ['6608c6546b3c6bde084fc1d34b1163c1']='health.d/retroshare.conf'
   ['1c12b678ab65f271a96da1bbd0a1ab1c']='health.d/softnet.conf'
   ['2472e49550326f7142e2c425ccbca005']='health.d/softnet.conf'
+  ['28df44a90e8ea4c6156314c03e88bf44']='health.d/softnet.conf'
   ['312b4b8e2805e19cf9be554b319567d6']='health.d/softnet.conf'
   ['565f11c38ae6bd5cc9d3c2adb542bc1b']='health.d/softnet.conf'
   ['6398ef37a15cb6a0bc921f58948d2b39']='health.d/softnet.conf'
@@ -152,20 +167,14 @@ declare -A configs_signatures=(
   ['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'
-  ['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'
@@ -181,11 +190,6 @@ declare -A configs_signatures=(
   ['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'
@@ -227,6 +231,7 @@ declare -A configs_signatures=(
   ['d7e0bd12d4a60a761dcab3531a841711']='python.d/phpfpm.conf'
   ['142a5b693d34b0308bb0b8aec71fad79']='python.d/postfix.conf'
   ['ca249db7a0637d55abb938d969f9b486']='python.d/postfix.conf'
+  ['0cd4e1fb57497e4d4c2451a9e58f724d']='python.d/redis.conf'
   ['39571e9fad9b759200c5d5b2ee13feb4']='python.d/redis.conf'
   ['777f4da70f461ef675bde07fb3644312']='python.d/redis.conf'
   ['b915126262d08aa9da81de539a58a3fb']='python.d/redis.conf'
index d3643d753cbd05afcbbab31f43b89c0233ae7254..60bcf3f28021b899582ed7e93ddd89b3df073ab8 100644 (file)
@@ -37,14 +37,16 @@ updates first.
 
 * rename `contrib/debian/control.wheezy` to `contrib/debian/control`.
 
+* change `control.wheezy from contrib/Makefile* to control`.
+
 * uncomment `EXTRA_OPTS="-P /var/run/netdata.pid"` in
  `contrib/debian/netdata.default`
 
 * edit `contrib/debian/netdata.init` and change `PIDFILE` to
   `/var/run/netdata.pid`
 
-* uncomment `postrotate` in `system/netdata.logrotate.in` and change
-  `try-restart` to `restart`
+* remove `dpkg-statoverride --update --add --force root netdata 0775 /var/lib/netdata/registry` from
+  `contrib/debian/netdata.postinst.in`. If you are going to handle the unique id file differently.
 
 Then proceed as the main instructions above.
 
index 5a4e41af36fc72eb4cfa5d5cfe47fe4806f9f366..97341774036e001a30a5924507e651e1dd64437a 100644 (file)
@@ -21,12 +21,15 @@ case "$1" in
               dpkg-statoverride --update --add root netdata 0755 /var/lib/netdata/www
           fi
 
+          fi
+
           if ! dpkg-statoverride --list /var/cache/netdata >/dev/null 2>&1; then
               dpkg-statoverride --update --add netdata netdata 0755 /var/cache/netdata
           fi
 
         fi
 
+        dpkg-statoverride --update --add --force root netdata 0775 /var/lib/netdata/registry
         chown -R root:netdata /usr/share/netdata/*
         chown -R root:netdata /usr/lib/@DEB_HOST_MULTIARCH@/netdata/plugins.d
         setcap cap_dac_read_search,cap_sys_ptrace+ep /usr/lib/@DEB_HOST_MULTIARCH@/netdata/plugins.d/apps.plugin
@@ -38,4 +41,3 @@ esac
 #DEBHELPER#
 
 exit 0
-
diff --git a/diagrams/build.sh b/diagrams/build.sh
new file mode 100755 (executable)
index 0000000..53f0ea7
--- /dev/null
@@ -0,0 +1,20 @@
+#!/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
diff --git a/diagrams/config.puml b/diagrams/config.puml
new file mode 100644 (file)
index 0000000..0ce0932
--- /dev/null
@@ -0,0 +1,46 @@
+
+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
+}
+
diff --git a/diagrams/registry.puml b/diagrams/registry.puml
new file mode 100644 (file)
index 0000000..51a337f
--- /dev/null
@@ -0,0 +1,40 @@
+@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
index c3b97243be54217d2c9a4a86ae076fd32479ce1e..930243c4c4d734f14d166092b64461345fad5a66 100755 (executable)
@@ -15,6 +15,12 @@ fi
 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}"
 
@@ -133,7 +139,7 @@ get_git_config_signatures() {
 
     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}"
@@ -427,7 +433,7 @@ if [ -f src/netdata ]
 fi
 
 echo >&2 "Compiling netdata ..."
-run make || exit 1
+run make -j${processors} || exit 1
 
 if [ "${BASH_VERSINFO[0]}" -ge "4" ]
 then
@@ -713,35 +719,35 @@ isnetdata() {
 }
 
 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
 }
 
@@ -755,7 +761,7 @@ stop_all_netdata() {
         $(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
 }
 
@@ -854,13 +860,13 @@ if [ ! -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf" ]
     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"
index 278850e06c39987bc0140920d31e55eac081344e..e5677510023bd7818de52f6025b5b62f3051bd9b 100644 (file)
@@ -22,16 +22,20 @@ Release:    1%{?dist}
 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
@@ -147,7 +151,9 @@ rm -rf $RPM_BUILD_ROOT
 
 %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}
 
index 19b8ce29bdf10ade8596d18dde36ae8ae7482932..54e80388a333b909f695664c28aa5f506c005977 100644 (file)
@@ -6,8 +6,8 @@
 // http://jpmens.net/2013/03/18/json-in-bind-9-s-statistics-server/
 // https://ftp.isc.org/isc/bind/9.10.3/doc/arm/Bv9ARM.ch06.html#statistics
 
-// example configuration in /etc/netdata/named.conf
-// the module supports auto-detection if bind is running in localhost
+// example configuration in /etc/netdata/node.d/named.conf
+// the module supports auto-detection if bind is running at localhost
 
 /*
 {
index 6183993b5ba03898cca4e5e982e8fccf2551e922..706f75f4f6140aeb8c685bc84c87609311f71ee1 100644 (file)
@@ -1,5 +1,10 @@
 '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');
@@ -108,7 +113,8 @@ var netdata = {
                                });
 
                                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);
                                });
 
@@ -127,18 +133,33 @@ var netdata = {
                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
index 3dd17a5c123172ff1732b636c431bf34b79df258..3d99943d4d0cadee643f3e31cef2f2cc89a25a9f 100644 (file)
@@ -3,7 +3,7 @@
 // This program will connect to one or more SMA Sunny Webboxes
 // to get the Solar Power Generated (current, today, total).
 
-// example configuration in /etc/netdata/sma_webbox.conf
+// example configuration in /etc/netdata/node.d/sma_webbox.conf
 /*
 {
     "enable_autodetect": false,
index ddc8985270be68ebd6c4e3a8d17e415f8273fdef..d1563e19681a0a80e040b7dd2eb7cd2a8cdfcf33 100644 (file)
@@ -2,7 +2,7 @@
 
 // This program will connect to one or more SNMP Agents
 
-// example configuration in /etc/netdata/snmp.conf
+// example configuration in /etc/netdata/node.d/snmp.conf
 /*
 {
     "enable_autodetect": false,
index cba137d7fb9f3d762db78fadb1a76877e09867a7..7d3bc44f77e6d2088423cf20fcba760dd502cb8e 100644 (file)
@@ -14,6 +14,7 @@ dist_plugins_SCRIPTS = \
        cgroup-name.sh \
        charts.d.dryrun-helper.sh \
        charts.d.plugin \
+       fping.plugin \
        node.d.plugin \
        python.d.plugin \
        tc-qos-helper.sh \
index ac9b29336e2bb2c4b40454e8790d65e31ebb5ffc..a41c55bc70c520896bb0e9e0c059da51d1f3c6df 100755 (executable)
@@ -5,7 +5,7 @@
 # (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"
@@ -63,14 +105,14 @@ info="${18}"       # a short description of the alarm
 # 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
 
@@ -286,8 +328,7 @@ 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" -a "${SEND_PUSHBULLET}" != "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
 
 # -----------------------------------------------------------------------------
@@ -396,10 +437,10 @@ send_email() {
 
         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
@@ -441,10 +482,10 @@ send_pushover() {
 
             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
 
@@ -511,13 +552,13 @@ send_telegram() {
 
             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
 
@@ -579,10 +620,10 @@ EOF
         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
 
@@ -724,15 +765,11 @@ SENT_PUSHBULLET=$?
 # send the telegram.org message
 
 # https://core.telegram.org/bots/api#formatting-options
-telegram_message="<b>${severity}"
-[ "${status_message}" != "recovered" ] && telegram_message="${telegram_message}, ${status_message}"
-telegram_message="${telegram_message}
-${chart} (${family})</b>
+send_telegram "${TELEGRAM_BOT_TOKEN}" "${to_telegram}" "${host} ${status_message} - <b>${name//_/ }</b>
+${chart} (${family})
 <a href=\"${goto_url}\">${alarm}</a>
 <i>${info}</i>"
 
-send_telegram "${TELEGRAM_BOT_TOKEN}" "${to_telegram}" "${telegram_message}"
-
 SENT_TELEGRAM=$?
 
 # -----------------------------------------------------------------------------
index 1c6f564b4c820dab31282939b63bb9ddcbd671c5..9bb3bcabbe7a426d0789ae8658adcfe08260b132 100755 (executable)
@@ -1,17 +1,66 @@
 #!/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}" ]
@@ -19,15 +68,15 @@ 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
 }
@@ -36,10 +85,10 @@ function get_name_api {
     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
@@ -62,10 +111,10 @@ if [ -z "${NAME}" ]
             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
@@ -74,5 +123,5 @@ if [ -z "${NAME}" ]
     [ ${#NAME} -gt 100 ] && NAME="${NAME:0:100}"
 fi
 
-echo >&2 "${0}: cgroup '${CGROUP}' is called '${NAME}'"
+info "cgroup '${CGROUP}' is called '${NAME}'"
 echo "${NAME}"
index df9998ecec1ab838caba4c9c743b9b35efe8d46a..00206f95f96fe9844585b51c8d72b89d4e78396a 100755 (executable)
@@ -1,36 +1,95 @@
 #!/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
 }
 
@@ -43,9 +102,17 @@ require_cmd grep || exit 1
 require_cmd egrep || exit 1
 require_cmd mktemp || exit 1
 require_cmd awk || exit 1
+require_cmd timeout || exit 1
+require_cmd curl || exit 1
+
+# -----------------------------------------------------------------------------
+
+[ $(( ${BASH_VERSINFO[0]} )) -lt 4 ] && fatal "BASH version 4 or later is required, but found version: ${BASH_VERSION}. Please upgrade."
+
+info "started from '$PROGRAM_FILE' with options: $*"
 
 # -----------------------------------------------------------------------------
-# insternal defaults
+# internal defaults
 # netdata exposes a few environment variables for us
 
 pluginsd="${NETDATA_PLUGINS_DIR}"
@@ -97,7 +164,6 @@ enable_all_charts="yes"
 # -----------------------------------------------------------------------------
 # parse parameters
 
-debug=0
 check=0
 chart_only=
 while [ ! -z "$1" ]
@@ -143,9 +209,7 @@ do
         continue
     fi
 
-    echo >&2 "Cannot understand parameter $1. Aborting."
-    echo "DISABLE"
-    exit 1
+    fatal "Cannot understand parameter $1. Aborting."
 done
 
 
@@ -173,17 +237,13 @@ mysleep="sleep"
 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
@@ -204,12 +264,7 @@ update_every=$(( update_every + 1 - 1)) # makes sure it is a number
 test $update_every -eq 0 && update_every=1 # if it is zero, make it 1
 
 # check the charts.d directory
-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
@@ -221,6 +276,35 @@ fixid() {
         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}
@@ -230,8 +314,6 @@ float2int() {
     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 ))
 
@@ -277,7 +359,6 @@ float2int() {
 
     # store the result
     FLOAT2INT_RESULT=$(( (a * m) + b ))
-    #echo >&2 "FLOAT2INT_RESULT='${FLOAT2INT_RESULT}'"
 }
 
 
@@ -286,7 +367,7 @@ float2int() {
 
 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"
 }
@@ -316,6 +397,8 @@ all_enabled_charts() {
 
     for chart in $( all_charts )
     do
+        MODULE_NAME="${chart}"
+
         eval "enabled=\$$chart"
         if [ -z "${enabled}" ]
             then
@@ -327,35 +410,38 @@ all_enabled_charts() {
 
         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
 
@@ -364,7 +450,7 @@ all_enabled_charts() {
         #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
@@ -374,19 +460,19 @@ all_enabled_charts() {
         #   "$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
 
@@ -394,19 +480,22 @@ suffix_update_every="_update_every"
 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"
@@ -419,13 +508,14 @@ do
     $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"
 
 
 # -----------------------------------------------------------------------------
@@ -438,7 +528,7 @@ test $debug -eq 1 && debug_time=tellwork
 # 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
@@ -450,41 +540,19 @@ then
     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
@@ -492,28 +560,26 @@ cd "$TMP_DIR" || exit 1
 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() {
@@ -552,12 +618,12 @@ 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}
 
@@ -576,7 +642,6 @@ global_update() {
                 fi
 
                 exec_start_ms=$now_ms
-                #echo >&2 "                              EXEC: $chart$charts_update $dt"
                 $chart$charts_update $dt
                 ret=$?
 
@@ -596,9 +661,9 @@ global_update() {
 
                     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
@@ -606,6 +671,7 @@ global_update() {
                 next_charts+=($chart)
             fi
         done
+        MODULE_NAME="${chart}"
 
         # wait the time you are required to
         next_ms=$((now_ms + (update_every * 1000 * 100) ))
@@ -625,18 +691,17 @@ global_update() {
                 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
diff --git a/plugins.d/fping.plugin b/plugins.d/fping.plugin
new file mode 100755 (executable)
index 0000000..32e1b71
--- /dev/null
@@ -0,0 +1,122 @@
+#!/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."
index 21b04384e8ca87607e36cd747e9966d59351c3fa..8b7047fcbbbfdb5423cb10593a084576fca7e9e2 100755 (executable)
@@ -8,6 +8,11 @@
 // 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';
@@ -30,6 +35,7 @@ process.mainModule.paths.unshift(NODE_D_DIR);
 
 var fs = require('fs');
 var url = require('url');
+var util = require('util');
 var http = require('http');
 var path = require('path');
 var extend = require('extend');
@@ -61,8 +67,6 @@ extend(true, netdata.options, {
 
     update_every: NETDATA_UPDATE_EVERY,
 
-    exit_after_ms: 3600 * 4 * 1000,
-
     paths: {
         plugins: NETDATA_PLUGINS_DIR,
         config: NETDATA_CONFIG_DIR,
@@ -79,8 +83,15 @@ netdata.options.config_filename = pluginConfig(__filename);
 try {
     netdata.options_loaded = JSON.parse(fs.readFileSync(netdata.options.config_filename, 'utf8'));
     extend(true, netdata.options, netdata.options_loaded);
-    console.error('merged netdata object:');
-    console.error(netdata);
+
+    if(!netdata.options.paths.plugins)
+        netdata.options.paths.plugins = NETDATA_PLUGINS_DIR;
+
+    if(!netdata.options.paths.config)
+        netdata.options.paths.config = NETDATA_CONFIG_DIR;
+
+    // console.error('merged netdata object:');
+    // console.error(util.inspect(netdata, {depth: 10}));
 }
 catch(e) {
     netdata.error('Cannot read configuration file ' + netdata.options.config_filename + ': ' + e.message + ', using internal defaults.');
index 9caef85f720cfeae0a8fb0bf54ed9efbd31898cc..ffe995f7d316616e1fc963d97078e9afad26f1b7 100755 (executable)
@@ -1,11 +1,59 @@
 #!/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 )"
 
@@ -39,8 +87,7 @@ loopsleepms() {
 
 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=
index a3c7b4fac9f0e8f04a0df899302d823b1ae898ac..cb06f67fe75266ddbf0ea902e3efeff215129437 100644 (file)
@@ -158,45 +158,47 @@ Dovecot unix socket with R/W permissions for user netdata or dovecot with config
  
 Module gives information with following charts:
 
-1. **logins and sessions**
- * logins
+1. **sessions**
  * active sessions
 
-2. **commands** - number of IMAP commands 
+2. **logins**
+ * logins
+
+3. **commands** - number of IMAP commands 
  * commands
  
-3. **Faults**
+4. **Faults**
  * minor
  * major
  
-4. **Context Switches** 
+5. **Context Switches** 
  * volountary
  * involountary
  
-5. **disk** in bytes/s
+6. **disk** in bytes/s
  * read
  * write
  
-6. **bytes** in bytes/s
+7. **bytes** in bytes/s
  * read
  * write
  
-7. **number of syscalls** in syscalls/s
+8. **number of syscalls** in syscalls/s
  * read
  * write
 
-8. **lookups** - number of lookups per second
+9. **lookups** - number of lookups per second
  * path
  * attr
 
-9. **hits** - number of cache hits 
+10. **hits** - number of cache hits 
  * hits
 
-10. **attempts** - authorization attemts
+11. **attempts** - authorization attemts
  * success
  * failure
 
-11. **cache** - cached authorization hits
+12. **cache** - cached authorization hits
  * hit
  * miss
  
index 586478cdabac0acf0c2e78730e5d5caeed5bde9b..c05cb0c2e96f9a3ace38c570011549bbb7f81d1f 100644 (file)
@@ -10,7 +10,7 @@ priority = 60000
 retries = 60
 
 # charts order (can be overridden if you want less charts, or different order)
-ORDER = ['sessions', 'commands',
+ORDER = ['sessions', 'logins', 'commands',
          'faults',
          'context_switches',
          'disk', 'bytes', 'syscalls',
@@ -19,11 +19,15 @@ ORDER = ['sessions', 'commands',
 
 CHARTS = {
     'sessions': {
-        'options': [None, "logins and sessions", 'number', 'IMAP', 'dovecot.sessions', 'line'],
+        'options': [None, "active sessions", 'number', 'IMAP', 'dovecot.sessions', 'line'],
         'lines': [
-            ['num_logins', 'logins', 'absolute'],
             ['num_connected_sessions', 'active sessions', 'absolute']
         ]},
+    'logins': {
+        'options': [None, "logins", 'number', 'IMAP', 'dovecot.logins', 'line'],
+        'lines': [
+            ['num_logins', 'logins', 'absolute']
+        ]},
     'commands': {
         'options': [None, "commands", "commands", 'IMAP', 'dovecot.commands', 'line'],
         'lines': [
index 1508e0965e58b8ca184400ebbc584b157c0d7c99..70c586f8e411a276b829406cddc75cbcf8a4b7cf 100644 (file)
@@ -608,7 +608,7 @@ class SocketService(SimpleService):
                 buf = self._sock.recv(4096)
                 if len(buf) == 0 or buf is None:  # handle server disconnect
                     break
-                data += buf.decode()
+                data += buf.decode(errors='ignore')
                 if self._check_raw_data(data):
                     break
             else:
index ecefba0a4fa185b38cb4e02b5a1c8b4f07651dc4..203751afdb051d39ab98c5f5b883c3dc772d017f 100644 (file)
@@ -24,7 +24,7 @@ def log_msg(msg_type, *args):
         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()
@@ -33,7 +33,7 @@ def log_msg(msg_type, *args):
     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()
index e1925ff5e9ad95c12f1fca49c3c249a67830aa3c..ce348d79479ab1b3dd7564ba5f98733a999648a8 100644 (file)
@@ -224,7 +224,7 @@ int sleep_usec(unsigned long long usec) {
 
     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 {
@@ -1121,7 +1121,7 @@ long get_system_cpus(void) {
 
     procfile_close(ff);
 
-    info("System has %d processors.", processors);
+    debug(D_SYSTEM, "System has %d processors.", processors);
     return processors;
 }
 
@@ -1152,7 +1152,7 @@ pid_t get_system_pid_max(void) {
     }
 
     procfile_close(ff);
-    info("System supports %d pids.", pid_max);
+    debug(D_SYSTEM, "System supports %d pids.", pid_max);
     return pid_max;
 }
 
index 9ffa8c8bc4efb7b43de058a0cae064c183f43145..b9721824f75f083f6f4475dd716b13ce7ffbb290 100644 (file)
@@ -64,6 +64,7 @@
 #include <time.h>
 #include <unistd.h>
 #include <uuid/uuid.h>
+#include <mntent.h>
 
 #ifdef STORAGE_WITH_MATH
 #include <math.h>
index 1c34405d826a89902580af25316b532ae9480f86..5bf6a1e0a0fffddcb69c669fce54ea9761c041c7 100644 (file)
@@ -138,7 +138,7 @@ void oom_score_adj(int score) {
     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) {
@@ -151,7 +151,7 @@ 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
@@ -214,14 +214,14 @@ int become_daemon(int dont_fork, const char *user)
     // 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) {
index 596b143a49d3b7d55fa82e90d0f2a1117a2869f6..53fbcdca9129575e12d5f40783245d81cb5fc1fc 100644 (file)
@@ -6,6 +6,7 @@ struct health_options {
     const char *health_default_exec;
     const char *health_default_recipient;
     const char *log_filename;
+    size_t log_entries_written;
     FILE *log_fp;
 };
 
@@ -13,6 +14,7 @@ static struct health_options health = {
     .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
 };
 
@@ -30,11 +32,11 @@ static inline int health_alarm_log_open(void) {
 
     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;
 }
 
@@ -45,51 +47,310 @@ static inline void health_alarm_log_close(void) {
     }
 }
 
-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
 
@@ -152,8 +413,8 @@ static inline void health_alarm_log(RRDHOST *host,
     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;
 
@@ -163,10 +424,9 @@ static inline void health_alarm_log(RRDHOST *host,
 
                 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);
@@ -1353,6 +1613,16 @@ static inline int health_parse_db_lookup(
     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);
@@ -1405,10 +1675,8 @@ int health_readfile(const char *path, const char *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] == '\\') {
@@ -1445,7 +1713,6 @@ int health_readfile(const char *path, const char *filename) {
             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)) {
@@ -1460,7 +1727,7 @@ int health_readfile(const char *path, const char *filename) {
 
             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;
@@ -1483,7 +1750,7 @@ int health_readfile(const char *path, const char *filename) {
                 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;
@@ -1497,12 +1764,12 @@ int health_readfile(const char *path, const char *filename) {
             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)) {
@@ -1512,14 +1779,14 @@ int health_readfile(const char *path, const char *filename) {
             }
             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);
                 }
             }
@@ -1527,7 +1794,7 @@ int health_readfile(const char *path, const char *filename) {
                 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);
                 }
             }
@@ -1561,43 +1828,43 @@ int health_readfile(const char *path, const char *filename) {
             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)) {
@@ -1612,12 +1879,12 @@ int health_readfile(const char *path, const char *filename) {
             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)) {
@@ -1627,14 +1894,14 @@ int health_readfile(const char *path, const char *filename) {
             }
             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);
                 }
             }
@@ -1642,7 +1909,7 @@ int health_readfile(const char *path, const char *filename) {
                 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);
                 }
             }
@@ -1676,43 +1943,43 @@ int health_readfile(const char *path, const char *filename) {
             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)) {
@@ -1798,7 +2065,16 @@ void health_init(void) {
         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();
 
@@ -1865,10 +2141,10 @@ static inline void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae, R
                    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,
@@ -2085,7 +2361,7 @@ void health_reload(void) {
     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
@@ -2121,25 +2397,36 @@ static inline int rrdcalc_value2status(calculated_number n) {
 }
 
 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];
@@ -2173,7 +2460,7 @@ static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) {
               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);
@@ -2189,7 +2476,7 @@ static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) {
     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);
@@ -2197,7 +2484,7 @@ done:
 }
 
 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),
@@ -2217,8 +2504,8 @@ static inline void health_alarm_log_process(RRDHOST *host) {
     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))
index 9d5834fcafefb8f69c178415e52651d7b79664ce..cebea49ba9b6ed7e9bfd1f4e5340fc843195cb1a 100644 (file)
@@ -264,10 +264,11 @@ typedef struct rrdcalctemplate {
 
 #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;
@@ -300,7 +301,7 @@ typedef struct alarm_entry {
     int old_status;
     int new_status;
 
-    uint32_t notifications;
+    uint32_t flags;
 
     int delay;
     time_t delay_up_to_timestamp;
index b7ec64bcb9463eb31b2eab07c37685c7f6a6d5ee..86716987aeba7eab4dbab471732d5dcafa4a9da3 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -200,7 +200,7 @@ int error_log_limit(int reset) {
 
 void log_date(FILE *out)
 {
-        char outstr[24];
+        char outstr[26];
         time_t t;
         struct tm *tmp, tmbuf;
 
@@ -208,7 +208,7 @@ void log_date(FILE *out)
         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);
 }
@@ -222,7 +222,7 @@ void debug_int( const char *file, const char *function, const unsigned long line
 
     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');
@@ -249,8 +249,8 @@ void info_int( const char *file, const char *function, const unsigned long line,
     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 );
 
@@ -298,8 +298,8 @@ void error_int( const char *prefix, const char *file, const char *function, cons
     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 );
 
@@ -325,8 +325,8 @@ void fatal_int( const char *file, const char *function, const unsigned long line
     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 );
 
index 3a022fc93959cba965940bb66b21c9ad6943fac5..b7323fcf47bd5e73ebe3542e32ddb378ff10f536 100644 (file)
--- a/src/log.h
+++ b/src/log.h
@@ -25,6 +25,7 @@
 #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
index 3e6aa5046845b29fbf552c01c2364829c060db24..1a81993873f9092590cc2c38ba6b73f8182d5a4c 100644 (file)
@@ -7,7 +7,7 @@ void netdata_cleanup_and_exit(int ret) {
 
     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
@@ -169,7 +169,7 @@ void kill_childs()
     }
 
     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);
     }
@@ -460,7 +460,8 @@ int main(int argc, char **argv)
         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);
         }
 
@@ -520,7 +521,7 @@ int main(int argc, char **argv)
 
         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 {
@@ -531,7 +532,7 @@ int main(int argc, char **argv)
 
         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);
@@ -635,7 +636,7 @@ int main(int argc, char **argv)
     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 */
@@ -655,7 +656,7 @@ int main(int argc, char **argv)
         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);
     }
 
     // ------------------------------------------------------------------------
@@ -692,7 +693,7 @@ int main(int argc, char **argv)
         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);
@@ -700,7 +701,7 @@ int main(int argc, char **argv)
             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);
     }
 
     // ------------------------------------------------------------------------
@@ -716,7 +717,7 @@ int main(int argc, char **argv)
     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);
         }
index ad8d7596f5ea9383ea3584e9f9f57e71ab101fa8..8448b7311d18049b185f686b2b92a2abb00fa527 100644 (file)
@@ -138,7 +138,7 @@ FILE *mypopen(const char *command, pid_t *pidptr)
             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);
 }
index 459d5a133b29fd2e5024e20b7b37ab92b1c6f2df..6a5bf0a35cf969b153b3c073e5263debf0ec5857 100644 (file)
@@ -6,13 +6,19 @@
 #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;
@@ -31,6 +37,152 @@ static struct disk {
 
 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] = "";
@@ -114,22 +266,21 @@ static struct disk *get_disk(unsigned long major, unsigned long minor, char *dis
 
     // 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
@@ -189,8 +340,6 @@ static inline int select_positive_option(int option1, int option2) {
 
 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,
@@ -245,27 +394,29 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
     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
@@ -372,7 +523,6 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
 
                         if(d->mount_point)
                             def_performance = select_positive_option(def_performance, global_enable_performance_for_virtual_mountpoints);
-
                         break;
                 }
 
@@ -626,74 +776,42 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
             }
         }
 
+/*
         // --------------------------------------------------------------------------
         // 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;
index 51aea7aee1a9b7a653dba8f15f16d6777fae5c4f..22be1aa630a51718bf49b8d5f1aa608afae620e1 100644 (file)
@@ -1,5 +1,48 @@
 #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) {
@@ -72,6 +115,7 @@ void mountinfo_free(struct mountinfo *mi) {
     freez(mi->root);
     freez(mi->mount_point);
     freez(mi->mount_options);
+    freez(mi->persistent_id);
 
 /*
     if(mi->optional_fields_count) {
@@ -138,14 +182,6 @@ struct mountinfo *mountinfo_read() {
 
         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++;
@@ -155,12 +191,15 @@ struct mountinfo *mountinfo_read() {
 
         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);
 
@@ -170,6 +209,10 @@ struct mountinfo *mountinfo_read() {
         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
@@ -214,29 +257,124 @@ struct mountinfo *mountinfo_read() {
             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;
 }
index c2d9688c19e836e221f7e0f445fc961abbad3b69..d47b6112562f70dca15331a1828d1e5bffa2aba8 100644 (file)
@@ -1,12 +1,22 @@
 #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;
 
@@ -27,6 +37,10 @@ struct mountinfo {
 
     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;
 };
 
index a0fb629ada069cf3becf5da0a356f1c118ab1f58..bce7c348fa0339933f98c20070fb8f1890eb5d8c 100644 (file)
@@ -743,8 +743,7 @@ static inline void registry_log_recreate_nolock(void) {
 }
 
 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
@@ -755,8 +754,10 @@ int registry_log_load(void) {
     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++;
 
@@ -766,7 +767,7 @@ int registry_log_load(void) {
 
                     // 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';
@@ -781,7 +782,7 @@ int registry_log_load(void) {
                     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';
@@ -800,7 +801,7 @@ int registry_log_load(void) {
                     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;
             }
         }
@@ -1471,7 +1472,7 @@ int registry_save(void) {
     // 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)
index 474b5915db753f3becf23798bdf789d868249206..b2c1a05583ab535f4ed22a7164ad164e4e913c3b 100644 (file)
@@ -1158,7 +1158,7 @@ inline static void rrdr_free(RRDR *r)
     freez(r);
 }
 
-inline void rrdr_done(RRDR *r)
+static inline void rrdr_done(RRDR *r)
 {
     r->rows = r->c + 1;
     r->c = 0;
index 0cf9eeb6a662daff5156e606aa5ca41730c7875c..c16acfa00e88b8631d3dfe82c7a609d03fbdc3f5 100644 (file)
@@ -14,7 +14,7 @@ int web_enable_gzip = 1, web_gzip_level = 3, web_gzip_strategy = Z_DEFAULT_STRAT
 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;
@@ -29,7 +29,7 @@ inline int web_client_crock_socket(struct web_client *w) {
     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;
@@ -2210,7 +2210,7 @@ void web_client_process(struct web_client *w) {
     if(w->mode == WEB_CLIENT_MODE_OPTIONS) {
         buffer_strcat(w->response.header_output,
             "Access-Control-Allow-Methods: GET, OPTIONS\r\n"
-            "Access-Control-Allow-Headers: accept, x-requested-with, origin, content-type, cookie\r\n"
+            "Access-Control-Allow-Headers: accept, x-requested-with, origin, content-type, cookie, pragma, cache-control\r\n"
             "Access-Control-Max-Age: 1209600\r\n" // 86400 * 14
             );
     }
index 214304aad113eacfb5a98faf13acd066c169b171..379094070760cb46cabf41596b11b1510466cdf1 100644 (file)
@@ -652,4 +652,4 @@ So, to avoid flashing the charts, we destroy and re-create the charts on each up
 <!-- <script> netdataServer = "http://box:19999"; </script> -->
 
 <!-- load the dashboard manager - it will do the rest -->
-<script type="text/javascript" src="dashboard.js?v20161008-1"></script>
+<script type="text/javascript" src="dashboard.js?v20161011-1"></script>
index d2122ce8272f35636a131f29a67fa689f8471532..1e42bf2dc7d14ce63a207e9a9218c4781bb101ff 100644 (file)
                 url: this.data_url,
                 cache: false,
                 async: true,
+                headers: {
+                    'Cache-Control': 'no-cache, no-store',
+                    'Pragma': 'no-cache'
+                },
                 xhrFields: { withCredentials: true } // required for the cookie
             })
             .done(function(data) {
         state.easyPieChartUnits.style.top = unittop.toString() + 'px';
         state.element_chart.appendChild(state.easyPieChartUnits);
 
+        var barColor = self.data('easypiechart-barcolor');
+        if(typeof barColor === 'undefined' || barColor === null)
+            barColor = state.chartColors()[0];
+        else {
+            // <div ... data-easypiechart-barcolor="(function(percent){return(percent < 50 ? '#5cb85c' : percent < 85 ? '#f0ad4e' : '#cb3935');})" ...></div>
+            var tmp = eval(barColor);
+            if(typeof tmp === 'function')
+                barColor = tmp;
+        }
+
         chart.easyPieChart({
-            barColor: self.data('easypiechart-barcolor') || state.chartColors()[0], //'#ef1e25',
+            barColor: barColor,
             trackColor: self.data('easypiechart-trackcolor') || NETDATA.themes.current.easypiechart_track,
             scaleColor: self.data('easypiechart-scalecolor') || NETDATA.themes.current.easypiechart_scale,
             scaleLength: self.data('easypiechart-scalelength') || 5,
                 url: NETDATA.alarms.server + '/api/v1/alarms?' + what.toString(),
                 async: true,
                 cache: false,
+                headers: {
+                    'Cache-Control': 'no-cache, no-store',
+                    'Pragma': 'no-cache'
+                },
                 xhrFields: { withCredentials: true } // required for the cookie
             })
                 .done(function(data) {
                 url: NETDATA.alarms.server + '/api/v1/alarm_log?after=' + last_id.toString(),
                 async: true,
                 cache: false,
+                headers: {
+                    'Cache-Control': 'no-cache, no-store',
+                    'Pragma': 'no-cache'
+                },
                 xhrFields: { withCredentials: true } // required for the cookie
             })
                 .done(function(data) {
                     url: host + '/api/v1/registry?action=hello',
                     async: true,
                     cache: false,
+                    headers: {
+                        'Cache-Control': 'no-cache, no-store',
+                        'Pragma': 'no-cache'
+                    },
                     xhrFields: { withCredentials: true } // required for the cookie
                 })
                 .done(function(data) {
                     url: NETDATA.registry.server + '/api/v1/registry?action=access&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault), // + '&visible_url=' + encodeURIComponent(document.location),
                     async: true,
                     cache: false,
+                    headers: {
+                        'Cache-Control': 'no-cache, no-store',
+                        'Pragma': 'no-cache'
+                    },
                     xhrFields: { withCredentials: true } // required for the cookie
                 })
                 .done(function(data) {
                 url: NETDATA.registry.server + '/api/v1/registry?action=delete&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault) + '&delete_url=' + encodeURIComponent(delete_url),
                 async: true,
                 cache: false,
+                headers: {
+                    'Cache-Control': 'no-cache, no-store',
+                    'Pragma': 'no-cache'
+                },
                 xhrFields: { withCredentials: true } // required for the cookie
             })
                 .done(function(data) {
                 url: NETDATA.registry.server + '/api/v1/registry?action=search&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault) + '&for=' + machine_guid,
                 async: true,
                 cache: false,
+                headers: {
+                    'Cache-Control': 'no-cache, no-store',
+                    'Pragma': 'no-cache'
+                },
                 xhrFields: { withCredentials: true } // required for the cookie
             })
                 .done(function(data) {
                 url: NETDATA.registry.server + '/api/v1/registry?action=switch&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault) + '&to=' + new_person_guid,
                 async: true,
                 cache: false,
+                headers: {
+                    'Cache-Control': 'no-cache, no-store',
+                    'Pragma': 'no-cache'
+                },
                 xhrFields: { withCredentials: true } // required for the cookie
             })
                 .done(function(data) {
index 68df1eb9a8bc195d9b8934dff04a66b382b2a9e5..494593224293569dbcb1f09745c86e48e1133a4d 100644 (file)
@@ -697,8 +697,9 @@ netdataDashboard.context = {
 \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
@@ -706,7 +707,7 @@ netdataDashboard.context = {
     },\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
@@ -725,6 +726,19 @@ netdataDashboard.context = {
     },\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
index f5e40c008b3b01faa0afd5726891e93fe23f18a3..12b7241e5e2c6d0609ed6bb99c9869eb3f4def7e 100644 (file)
@@ -20,7 +20,7 @@
     <meta property="og:title" content="netdata - real-time performance monitoring, done right!"/>
     <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." />
 </head>
-<script type="text/javascript" src="dashboard.js?v20161008-1"></script>
+<script type="text/javascript" src="dashboard.js?v20161011-1"></script>
 <body>
 
 <div style="width: 100%; text-align: center;">
index e0af3890a2354c8ca6ad92a81f0e13ea7409ef3a..3903fb6eb45c5bb61027f391ac2bc17a1a04fcf6 100644 (file)
@@ -21,7 +21,7 @@
     <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." />
 </head>
 <script>var netdataTheme = 'slate';</script>
-<script type="text/javascript" src="http://my-netdata.io/dashboard.js?v20161008-1"></script>
+<script type="text/javascript" src="http://my-netdata.io/dashboard.js?v20161011-1"></script>
 <body>
 
 <div class="container" style="width: 90%; padding-top: 10px; text-align: center; color: #AAA">
index 019ca926e0346f53b99af4af3ac6c1d9dada7f89..3d05389121f9e6adbb409004b6d07cc416d55719 100644 (file)
@@ -52,7 +52,7 @@
         and that you have chown it to be owned by netdata:netdata
     -->
     <!-- <script type="text/javascript" src="http://my.server:19999/dashboard.js"></script> -->
-    <script type="text/javascript" src="dashboard.js?v20161008-1"></script>
+    <script type="text/javascript" src="dashboard.js?v20161011-1"></script>
 
     <script>
     // --- OPTIONS FOR THE CHARTS --
index 411cdb066562c8a77d9c07d031fc4e8be93933f0..9f6d54b4008a37dab6d41eb748b0781f624d5983 100644 (file)
@@ -18,7 +18,7 @@
     var netdataTheme = 'slate';
     var netdataShowHelp = true;
 </script>
-<script type="text/javascript" src="dashboard.js?v20161008-1"></script>
+<script type="text/javascript" src="dashboard.js?v20161011-1"></script>
 
 <script>
 var urlOptions = {
index 7802bd872d18fe28c9762441e97a709b3ea8dea3..0f83ca1d74b4af5be486c47c2045df4e020d91ff 100644 (file)
         hash: '#',
         theme: null,
         help: null,
+        update_always: false,
         pan_and_zoom: false,
         after: 0,
         before: 0,
         alarm_unique_id: 0,
         alarm_id: 0,
         alarm_event_id: 0,
+
         hasProperty: function(property) {
             // console.log('checking property ' + property + ' of type ' + typeof(this[property]));
             return typeof this[property] !== 'undefined';
-        }
-    };
-
-    function netdataPanAndZoomCallback(status, after, before) {
-        urlOptions.pan_and_zoom = status;
-        urlOptions.after = after;
-        urlOptions.before = before;
-        netdataHashUpdate();
-    }
+        },
 
-    function netdataHashUpdate() {
-        history.replaceState(null, '', netdataHash());
-    }
+        genHash: function() {
+            var hash = urlOptions.hash;
 
-    function netdataHash() {
-        var hash = urlOptions.hash;
+            if(urlOptions.pan_and_zoom === true) {
+                hash += ';after='  + urlOptions.after.toString() +
+                        ';before=' + urlOptions.before.toString();
+            }
 
-        if(urlOptions.pan_and_zoom === true) {
-            hash += ';after='  + urlOptions.after.toString() +
-                    ';before=' + urlOptions.before.toString();
-        }
+            if(urlOptions.theme !== null)
+                hash += ';theme=' + urlOptions.theme.toString();
 
-        if(urlOptions.theme !== null)
-            hash += ';theme=' + urlOptions.theme.toString();
+            if(urlOptions.help !== null)
+                hash += ';help=' + urlOptions.help.toString();
 
-        if(urlOptions.help !== null)
-            hash += ';help=' + urlOptions.help.toString();
+            if(urlOptions.update_always === true)
+                hash += ';update_always=true';
 
-        return hash;
-    }
+            return hash;
+        },
 
-    function netdataHashParse() {
-        var variables = document.location.hash.split(';');
-        var len = variables.length;
-        while(len--) {
-            if(len !== 0) {
-                var p = variables[len].split('=');
-                if(urlOptions.hasProperty(p[0]) && typeof p[1] !== 'undefined')
-                    urlOptions[p[0]] = decodeURIComponent(p[1]);
+        parseHash: function() {
+            var variables = document.location.hash.split(';');
+            var len = variables.length;
+            while(len--) {
+                if(len !== 0) {
+                    var p = variables[len].split('=');
+                    if(urlOptions.hasProperty(p[0]) && typeof p[1] !== 'undefined')
+                        urlOptions[p[0]] = decodeURIComponent(p[1]);
+                }
+                else {
+                    if(variables[len].length > 0)
+                        urlOptions.hash = variables[len];
+                }
             }
-            else {
-                if(variables[len].length > 0)
-                    urlOptions.hash = variables[len];
+
+            var booleans = [ 'nowelcome', 'show_alarms', 'pan_and_zoom', 'update_always' ];
+            len = booleans.length;
+            while(len--) {
+                if(urlOptions[booleans[len]] === 'true' || urlOptions[booleans[len]] === true || urlOptions[booleans[len]] === '1' || urlOptions[booleans[len]] === 1)
+                    urlOptions[booleans[len]] = true;
+                else
+                    urlOptions[booleans[len]] = false;
             }
-        }
 
-        var booleans = [ 'nowelcome', 'show_alarms', 'pan_and_zoom' ];
-        len = booleans.length;
-        while(len--) {
-            if(urlOptions[booleans[len]] === 'true' || urlOptions[booleans[len]] === true || urlOptions[booleans[len]] === '1' || urlOptions[booleans[len]] === 1)
-                urlOptions[booleans[len]] = true;
+            if(urlOptions.before > 0 && urlOptions.after > 0) {
+                urlOptions.pan_and_zoom = true;
+                urlOptions.nowelcome = true;
+            }
             else
-                urlOptions[booleans[len]] = false;
-        }
+                urlOptions.pan_and_zoom = false;
+
+            // console.log(urlOptions);
+        },
+
+        hashUpdate: function() {
+            history.replaceState(null, '', urlOptions.genHash());
+        },
 
-        if(urlOptions.before > 0 && urlOptions.after > 0) {
-            urlOptions.pan_and_zoom = true;
-            urlOptions.nowelcome = true;
+        netdataPanAndZoomCallback: function(status, after, before) {
+            urlOptions.pan_and_zoom = status;
+            urlOptions.after = after;
+            urlOptions.before = before;
+            urlOptions.hashUpdate();
         }
-        else
-            urlOptions.pan_and_zoom = false;
 
-        // console.log(urlOptions);
-    }
+    };
 
-    netdataHashParse();
+    urlOptions.parseHash();
 
     // --------------------------------------------------------------------
     // check options that should be processed before loading netdata.js
         if(url.indexOf('#') !== -1)
             url = url.substring(0, url.indexOf('#'));
 
-        var hash = netdataHash();
+        var hash = urlOptions.genHash();
 
         // console.log('netdataURL: ' + url + hash);
 
         // ------------------------------------------------------------------------
 
         // callback for us to track PanAndZoom operations
-        NETDATA.globalPanAndZoom.callback = netdataPanAndZoomCallback;
+        NETDATA.globalPanAndZoom.callback = urlOptions.netdataPanAndZoomCallback;
 
         // let it run (update the charts)
         NETDATA.unpause();
             if(typeof hash === 'string' && hash.substring(0, 1) === '#' && urlOptions.hash.startsWith(hash + '_submenu_') === false) {
                 urlOptions.hash = hash;
                 //console.log(urlOptions.hash);
-                netdataHashUpdate();
+                urlOptions.hashUpdate();
             }
             //else console.log('hash: not accepting ' + hash);
             //}
         $('#concurrent_refreshes').change(function()            { NETDATA.setOption('concurrent_refreshes', $(this).prop('checked')); });
         $('#sync_selection').change(function()                  { NETDATA.setOption('sync_selection', $(this).prop('checked')); });
         $('#sync_pan_and_zoom').change(function()               { NETDATA.setOption('sync_pan_and_zoom', $(this).prop('checked')); });
-        $('#stop_updates_when_focus_is_lost').change(function() { NETDATA.setOption('stop_updates_when_focus_is_lost', $(this).prop('checked')); });
+        $('#stop_updates_when_focus_is_lost').change(function() {
+            urlOptions.update_always = !$(this).prop('checked');
+            urlOptions.hashUpdate();
+
+            NETDATA.setOption('stop_updates_when_focus_is_lost', !urlOptions.update_always);
+        });
         $('#smooth_plot').change(function()                     { NETDATA.setOption('smooth_plot', $(this).prop('checked')); });
         $('#pan_and_zoom_data_padding').change(function()       { NETDATA.setOption('pan_and_zoom_data_padding', $(this).prop('checked')); });
         $('#show_help').change(function()                       {
             urlOptions.help = $(this).prop('checked');
+            urlOptions.hashUpdate();
+
             NETDATA.setOption('show_help', urlOptions.help);
             netdataReload();
         });
         // it reloads the page
         $('#netdata_theme_control').change(function() {
             urlOptions.theme = $(this).prop('checked')?'slate':'white';
+            urlOptions.hashUpdate();
+
             if(setTheme(urlOptions.theme))
                 netdataReload();
         });
         });
 
         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; }
         });
         if(isdemo()) {
             document.getElementById('masthead').style.display = 'block';
         }
+        else {
+            if(urlOptions.update_always === true)
+                NETDATA.setOption('stop_updates_when_focus_is_lost', !urlOptions.update_always);
+        }
     }
 
     // our entry point
     </div>
 </body>
 </html>
-<script type="text/javascript" src="dashboard.js?v20161008-1"></script>
+<script type="text/javascript" src="dashboard.js?v20161011-1"></script>
index c2ed74d44c459d6bf332bf97ff75e9e89bfac3e3..ad424abad8726d93f94bdb0412d08bf9e823b608 100644 (file)
@@ -3,7 +3,7 @@
     "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": [
@@ -90,7 +90,7 @@
                     {
                         "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"
                         },
index 370ebd2461e6d1c74cc2763f395908b612120f17..8c96207908960336e74f4a48cb7e139b4fd2843a 100644 (file)
@@ -2,7 +2,7 @@ swagger: '2.0'
 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
@@ -66,7 +66,7 @@ paths:
           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
@@ -74,14 +74,14 @@ paths:
           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
index 767d97aea235e591a85f7318b322091e3891054d..8aa5fd3b25ed3073ffc802315b1b7fdd40e61353 100644 (file)
@@ -169,7 +169,7 @@ var netdataRegistryCallback = function(machines_array) {
     and that you have chown it to be owned by netdata:netdata
 -->
 <!-- <script type="text/javascript" src="http://my.server:19999/dashboard.js"></script> -->
-<script type="text/javascript" src="dashboard.js?v20161008-1"></script>
+<script type="text/javascript" src="dashboard.js?v20161011-1"></script>
 
 <script>
 // Set options for TV operation
index 24ccc02175a8fd3c660f9b5d59b4166045adbe64..8ce0caa49e1718756d67b7b0b9facc8cc503aa71 100644 (file)
@@ -50,7 +50,7 @@ var netdataTheme = 'slate'; // this is dark
     and that you have chown it to be owned by netdata:netdata
 -->
 <!-- <script type="text/javascript" src="http://my.server:19999/dashboard.js"></script> -->
-<script type="text/javascript" src="dashboard.js?v20161008-1"></script>
+<script type="text/javascript" src="dashboard.js?v20161011-1"></script>
 
 <script>
 // Set options for TV operation