]> arthur.barton.de Git - netdata.git/commitdiff
Merge branch 'master' into ab-debian
authorAlexander Barton <alex@barton.de>
Mon, 27 Feb 2017 13:34:08 +0000 (14:34 +0100)
committerAlexander Barton <alex@barton.de>
Mon, 27 Feb 2017 13:34:08 +0000 (14:34 +0100)
* master: (109 commits)
  mongodb_plugin: makesfiles update
  mongodb_plugin: python README update
  varnish_plugin: remove from conf file not ascii characters
  elasticsearch_plugin: code optimization
  create health directory in lib directory if it does not exist
  disable streaming by default
  better text
  add mirrored hosts to the my-netdata menu
  added comments and information at backends.c
  netdata-installer.sh now uses apps.plugin -t option to test if it is capable of reading /proc files
  added option -t to test if it has the permissions to read /proc/PID/io files
  remove obsolete permissions fixup for apps.plugin from the makefiles; fixes #1789
  re-arrange colors on CPU charts on freebsd; #1842
  added twitter on README.md
  web_log_plugin: requests per http version chart added
  apps.plugin: added calls counter and isolatored file counter only to new files processed; #1827
  mongodb_plugin: locks acquireCount charts added
  updated configs.signatures
  when not running as root, get the username we run as
  fixed wrong name of old config option
  ...

111 files changed:
CMakeLists.txt
README.md
conf.d/Makefile.am
conf.d/python.d/mongodb.conf [new file with mode: 0644]
conf.d/python.d/varnish.conf
conf.d/stream.conf [new file with mode: 0644]
configs.signatures
netdata-installer.sh
python.d/Makefile.am
python.d/README.md
python.d/elasticsearch.chart.py
python.d/mongodb.chart.py [new file with mode: 0644]
python.d/python_modules/base.py
python.d/web_log.chart.py
src/Makefile.am
src/adaptive_resortable_list.c
src/appconfig.c
src/appconfig.h
src/apps_plugin.c
src/backends.c
src/clocks.c
src/clocks.h
src/common.c
src/common.h
src/daemon.c
src/freebsd_sysctl.c
src/global_statistics.c
src/health.c
src/health.h
src/health_config.c [new file with mode: 0644]
src/health_json.c [new file with mode: 0644]
src/health_log.c [new file with mode: 0644]
src/ipc.c
src/log.c
src/log.h
src/macos_fw.c
src/macos_mach_smi.c
src/macos_sysctl.c
src/main.c
src/plugin_checks.c
src/plugin_freebsd.c
src/plugin_idlejitter.c
src/plugin_macos.c
src/plugin_nfacct.c
src/plugin_proc.c
src/plugin_proc_diskspace.c
src/plugin_tc.c
src/plugins_d.c
src/plugins_d.h
src/proc_diskstats.c
src/proc_interrupts.c
src/proc_loadavg.c
src/proc_meminfo.c
src/proc_net_dev.c
src/proc_net_ip_vs_stats.c
src/proc_net_netstat.c
src/proc_net_rpc_nfs.c
src/proc_net_rpc_nfsd.c
src/proc_net_snmp.c
src/proc_net_snmp6.c
src/proc_net_softnet_stat.c
src/proc_net_stat_conntrack.c
src/proc_net_stat_synproxy.c
src/proc_self_mountinfo.c
src/proc_softirqs.c
src/proc_stat.c
src/proc_sys_kernel_random_entropy_avail.c
src/proc_uptime.c
src/proc_vmstat.c
src/procfile.c
src/registry.c
src/registry.h
src/registry_init.c
src/registry_internals.c
src/registry_internals.h
src/registry_machine.c
src/registry_person.c
src/rrd.c
src/rrd.h
src/rrd2json.c
src/rrd2json.h
src/rrdcalc.c [new file with mode: 0644]
src/rrdcalctemplate.c [new file with mode: 0644]
src/rrddim.c [new file with mode: 0644]
src/rrddimvar.c [new file with mode: 0644]
src/rrdfamily.c [new file with mode: 0644]
src/rrdhost.c [new file with mode: 0644]
src/rrdpush.c [new file with mode: 0644]
src/rrdpush.h [new file with mode: 0644]
src/rrdset.c [new file with mode: 0644]
src/rrdsetvar.c [new file with mode: 0644]
src/rrdvar.c [new file with mode: 0644]
src/simple_pattern.c
src/socket.c
src/socket.h
src/sys_devices_system_edac_mc.c
src/sys_devices_system_node.c
src/sys_fs_cgroup.c
src/sys_kernel_mm_ksm.c
src/unit_test.c
src/web_api_old.c [new file with mode: 0644]
src/web_api_old.h [new file with mode: 0644]
src/web_api_v1.c [new file with mode: 0644]
src/web_api_v1.h [new file with mode: 0644]
src/web_buffer.c
src/web_client.c
src/web_client.h
src/web_server.c
src/web_server.h
web/demosites.html
web/index.html

index 893af80660953a7d7438136c2f45ca3fbb7d49ac..979144e06ed3ad8c17fd6f9c1031c5e46bbe30cd 100755 (executable)
@@ -28,6 +28,7 @@ set(NETDATA_SOURCE_FILES
         src/dictionary.h
         src/eval.c
         src/eval.h
+        src/freebsd_sysctl.c
         src/global_statistics.c
         src/global_statistics.h
         src/health.c
@@ -123,7 +124,7 @@ set(NETDATA_SOURCE_FILES
         src/web_client.h
         src/web_server.c
         src/web_server.h
-        )
+        src/rrdhost.c src/rrdfamily.c src/rrdset.c src/rrddim.c src/health_log.c src/health_config.c src/health_json.c src/rrdcalc.c src/rrdcalctemplate.c src/rrdvar.c src/rrddimvar.c src/rrdsetvar.c src/rrdpush.c src/rrdpush.h src/web_api_old.c src/web_api_old.h src/web_api_v1.c src/web_api_v1.h)
 
 set(APPS_PLUGIN_SOURCE_FILES
         src/appconfig.c
index faf72a07ef35949cca496fd1f0216929ce4d893e..240ab32be600590da0c7072e5da0076b261595f0 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# 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) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a994873f30d045b9b4b83606c3eb3498)](https://www.codacy.com/app/netdata/netdata?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=firehol/netdata&amp;utm_campaign=Badge_Grade) [![Code Climate](https://codeclimate.com/github/firehol/netdata/badges/gpa.svg)](https://codeclimate.com/github/firehol/netdata) [![Docker Pulls](https://img.shields.io/docker/pulls/titpetric/netdata.svg)](https://hub.docker.com/r/titpetric/netdata/)
+# 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) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a994873f30d045b9b4b83606c3eb3498)](https://www.codacy.com/app/netdata/netdata?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=firehol/netdata&amp;utm_campaign=Badge_Grade) [![Code Climate](https://codeclimate.com/github/firehol/netdata/badges/gpa.svg)](https://codeclimate.com/github/firehol/netdata) [![Docker Pulls](https://img.shields.io/docker/pulls/titpetric/netdata.svg)](https://hub.docker.com/r/titpetric/netdata/) [![Twitter Follow](https://img.shields.io/twitter/follow/linuxnetdata.svg?style=social&label=netdata%20on%20twitter)](https://twitter.com/linuxnetdata)
 > *New to netdata? Here is a live demo: [http://my-netdata.io](http://my-netdata.io)*
 
 **netdata** is a system for **distributed real-time performance and health monitoring**.
@@ -62,7 +62,7 @@ Netdata is featured at <b><a href="https://octoverse.github.com/" target="_blank
  - **Sophisticated alarming**<br/>
    supports dynamic thresholds, hysteresis, alarm templates,
    multiple role-based notification methods (such as email, slack.com,
-   pushover.net, pushbullet.com telegram.org, twilio.com, messagebird.com)
+   pushover.net, pushbullet.com, telegram.org, twilio.com, messagebird.com)
    
  - **Extensible**<br/>
    you can monitor anything you can get a metric for,
index c746a909d56e1b6994201238e22dbf375047a837..655d27a7378f6d5f6d406293292558c31164f3ae 100644 (file)
@@ -11,6 +11,7 @@ dist_config_DATA = \
     python.d.conf \
     health_alarm_notify.conf \
     health_email_recipients.conf \
+    stream.conf \
     $(NULL)
 
 nodeconfigdir=$(configdir)/node.d
@@ -39,6 +40,7 @@ dist_pythonconfig_DATA = \
     python.d/isc_dhcpd.conf \
     python.d/mdstat.conf \
     python.d/memcached.conf \
+    python.d/mongodb.conf \
     python.d/mysql.conf \
     python.d/nginx.conf \
     python.d/ovpn_status_log.conf \
diff --git a/conf.d/python.d/mongodb.conf b/conf.d/python.d/mongodb.conf
new file mode 100644 (file)
index 0000000..a19b657
--- /dev/null
@@ -0,0 +1,77 @@
+# netdata python.d.plugin configuration for mongodb
+#
+# This file is in YaML format. Generally the format is:
+#
+# name: value
+#
+# There are 2 sections:
+#  - global variables
+#  - one or more JOBS
+#
+# JOBS allow you to collect values from multiple sources.
+# Each source will have its own set of charts.
+#
+# JOB parameters have to be indented (using spaces only, example below).
+
+# ----------------------------------------------------------------------
+# Global Variables
+# These variables set the defaults for all JOBs, however each JOB
+# may define its own, overriding the defaults.
+
+# update_every sets the default data collection frequency.
+# If unset, the python.d.plugin default is used.
+# update_every: 1
+
+# priority controls the order of charts at the netdata dashboard.
+# Lower numbers move the charts towards the top of the page.
+# If unset, the default for python.d.plugin is used.
+# priority: 60000
+
+# retries sets the number of retries to be made in case of failures.
+# If unset, the default for python.d.plugin is used.
+# Attempts to restore the service are made once every update_every
+# and only if the module has collected values in the past.
+# retries: 5
+
+# ----------------------------------------------------------------------
+# JOBS (data collection sources)
+#
+# The default JOBS share the same *name*. JOBS with the same name
+# are mutually exclusive. Only one of them will be allowed running at
+# any time. This allows autodetection to try several alternatives and
+# pick the one that works.
+#
+# Any number of jobs is supported.
+#
+# All python.d.plugin JOBS (for all its modules) support a set of
+# predefined parameters. These are:
+#
+# job_name:
+#     name: myname     # the JOB's name as it will appear at the
+#                      # dashboard (by default is the job_name)
+#                      # JOBs sharing a name are mutually exclusive
+#     update_every: 1  # the JOB's data collection frequency
+#     priority: 60000  # the JOB's order on the dashboard
+#     retries: 5       # the JOB's number of restoration attempts
+#
+# Additionally to the above, mongodb also supports the following:
+#
+#     host: 'IP or HOSTNAME' # type <str> the host to connect to
+#     port: PORT             # type <int> the port to connect to
+#
+#  in all cases, the following can also be set:
+#
+#     user: 'username'       # the mongodb username to use
+#     pass: 'password'       # the mongodb password to use
+#
+
+# ----------------------------------------------------------------------
+# to connect to the mongodb on localhost, without a password:
+# ----------------------------------------------------------------------
+# AUTO-DETECTION JOBS
+# only one of them will run (they have the same name)
+
+local:
+    name : 'local'
+    host : '127.0.0.1'
+    port : 27017
index 56dc6334d4f97c774556aed3065d9b63d151d94e..c25f3010fd5f0898202ddf033b86e45f6ae324ec 100644 (file)
 #     retries: 5       # the JOB's number of restoration attempts
 #
 #
-#
-# The only you need is to add netdata to 'varnish' group
-#
-# Check it from cmd
-# id netdata
-# 
-# uid=999(netdata) gid=999(netdata) Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹=999(netdata),118(varnish)
-#
diff --git a/conf.d/stream.conf b/conf.d/stream.conf
new file mode 100644 (file)
index 0000000..641faaa
--- /dev/null
@@ -0,0 +1,119 @@
+# netdata configuration for aggregating data from remote hosts
+#
+# API keys authorize a pair of sending-receiving netdata servers.
+# Once their communication is authorized, they can exchange metrics for any
+# number of hosts.
+#
+# You can generate API keys, with the linux command: uuidgen
+#
+# -----------------------------------------------------------------------------
+# 1. SLAVE NETDATA - THE ONE THAT WILL BE SENDING METRICS
+
+[stream]
+       enabled = no
+
+       # where to send metrics to?
+       # A space separated list of IP:PORT is accepted. The first available will
+       # get the metrics.
+       # IPv6 addresses should be [IP]:PORT
+       destination =
+
+       # The API_KEY to use (as the sender)
+       api key =
+
+       # other options (uncomment and set)
+
+       # timeout seconds = 60
+       # default port = 19999
+       # buffer size bytes = 1048576
+       # reconnect delay seconds = 5
+       # initial clock resync iterations = 60
+       # free orphan hosts after seconds = 3600
+
+
+# -----------------------------------------------------------------------------
+# 2. MASTER NETDATA - THE ONE THAT WILL BE RECEIVING METRICS
+#
+#    You can have one API key per slave, or the same API key for all slaves.
+#
+#    All options below are used in this order:
+#
+#    a) MACHINE_GUID (settings for each machine)
+#    b) API_KEY      (settings for the API key)
+#    c) this netdata defaults (as in netdata.conf)
+#
+#    You can combine the above (the more specific setting will be used).
+
+# API key authentication
+# If the key is not listed here, it will not be able to connect.
+
+[API_KEY]
+    # Default settings for the API key
+
+    # You can disable the API key, by setting this to: no
+    # The default (for unknown API keys) is also: no
+    enabled = no
+
+    # The default history in entries, for all hosts using this API key.
+    # You can also set it per host below.
+    # If you don't set it here, the history size of the central netdata
+    # will be used
+    default history = 3600
+
+    # The default memory mode to be used for all hosts using this API key.
+    # You can also set it per host below.
+    # If you don't set it here, the memory mode of netdata.conf will be used.
+    # Valid modes:
+    #    save    save on exit, load on start
+    #    map     like swap (continuously syncing to disks)
+    #    ram     keep it in RAM, don't touch the disk
+    #    none    no database (passing through this netdata)
+    default memory mode = ram
+
+    # Shall we enable health monitoring for the hosts using this API key?
+    # 3 values:
+    #    yes     enable alarms
+    #    no      do not enable alarms
+    #    auto    enable alarms, only when the sending netdata is connected
+    # You can also set it per host, below.
+    # The default is the same as to netdata.conf
+    health enabled by default = auto
+
+    # postpone alarms for a short period after the sender is connected
+    default postpone alarms on connect seconds = 60
+
+    # need to route metrics differently? set these.
+    # the defaults are the ones at the [stream] section
+    #default proxy enabled = yes | no
+    #default proxy destination = IP:PORT IP:PORT ...
+    #default proxy api key = API_KEY
+
+
+# -----------------------------------------------------------------------------
+# Each netdata has a unique GUID - generated the first time netdata starts.
+# You can find it at /var/lib/netdata/registry/netdata.public.unique.id
+# The host sending data will have one. If it is static and you can find it,
+# you can give settings for each specific host here.
+
+[MACHINE_GUID]
+    # enable this host: yes | no
+    # THIS IS NOT A SECURITY MECHANISM - AN ATTACKER CAN SET ANY OTHER GUID.
+    # Use only the API key for security.
+    enabled = no
+
+    # The number of entries in the database
+    history = 3600
+
+    # The memory mode of the database: save | map | ram | none
+    memory mode = save
+
+    # Health / alarms control: yes | no | auto
+    health enabled = yes
+
+    # postpone alarms when the sender connects
+    postpone alarms on connect seconds = 60
+
+    # need to route metrics differently?
+    #proxy enabled = yes | no
+    #proxy destination = IP:PORT IP:PORT ...
+    #proxy api key = API_KEY
index e0bd81e45915146847ba5d2f164280ee0db11f79..edfe62f4be8cad4ad1374bc9f0d3bbdf523a15dc 100644 (file)
@@ -25,6 +25,7 @@ declare -A configs_signatures=(
   ['0c5e0fa364d7bdf7c16e8459a0544572']='health.d/netfilter.conf'
   ['0cd4e1fb57497e4d4c2451a9e58f724d']='python.d/redis.conf'
   ['0d29fe9919a2975107db1f2583344e7a']='health.d/mdstat.conf'
+  ['0dd38dcd2473ddb9f8b1b41147432d10']='health_alarm_notify.conf'
   ['0e59bc11d0a869ea0247c04c08c8d72e']='python.d/ipfs.conf'
   ['0ef8af1f358741afa7fd5d0ffabefaac']='charts.d/mysql.conf'
   ['10c3b525850a1cb9de760a8ee96fbc6e']='charts.d/opensips.conf'
@@ -96,6 +97,7 @@ declare -A configs_signatures=(
   ['3c9c47163e9d4dbcb0079b6232398f2f']='apps_groups.conf'
   ['3ca696189911fb38a0319ddd71e9a395']='python.d/phpfpm.conf'
   ['3cc6255457d4cba881ae0554ae5d9190']='health.d/squid.conf'
+  ['3d974ac9fdaa44d4527d6503bec35e34']='stream.conf'
   ['3f170e3343cd784983b019163393f5af']='health.d/nginx.conf'
   ['3fc45cc18e884c22482524dff6d27833']='python.d/hddtemp.conf'
   ['3fcc3c449ce8e0388f9c23ca07cab608']='health.d/backend.conf'
@@ -191,6 +193,7 @@ declare -A configs_signatures=(
   ['73a8e10dfe4183aca751e9e2a80dabe3']='node.d.conf'
   ['7454ed74511d7b9819dfe173f9020786']='python.d/redis.conf'
   ['749fe31362969d75f1ea66d15231d98d']='python.d/retroshare.conf'
+  ['7502c931aa9acbb92f54c67978d75983']='stream.conf'
   ['7596ae54d46ce199ac599429ef753caf']='health.d/cpu.conf'
   ['75a9c4b0b1c73956df55585eb0619f6c']='charts.d/ap.conf'
   ['769aa4cdcdc3d78d0328d1f9e4edcdf9']='python.d/mysql.conf'
@@ -233,6 +236,7 @@ declare -A configs_signatures=(
   ['8d0552371a7c9725a04196fa560813d1']='health.d/cpu.conf'
   ['8dc0bd0a70b5117454bd5f5b98f91c2c']='health.d/disks.conf'
   ['8f4f925c1e97dd164007495ec5135ffc']='health.d/fping.conf'
+  ['8f7b734ea0f89abf8acbb47c50234477']='health.d/web_log.conf'
   ['8fd472a854b0996327e8ed3562161182']='health_alarm_notify.conf'
   ['919911d13901d60a7580f5dfd7fc87bb']='health.d/ram.conf'
   ['91c757ef6be3abdb86906d9dbb9c217a']='fping.conf'
@@ -308,6 +312,7 @@ declare -A configs_signatures=(
   ['bb51112d01ff20053196a57632df8962']='apps_groups.conf'
   ['bba2f3886587f137ea08a6e63dd3d376']='python.d.conf'
   ['bda5517ea01640cfdfa0a27549619d6a']='health.d/memcached.conf'
+  ['bf66f113b2dd8d8fb444cbd5650f284c']='health_alarm_notify.conf'
   ['c004430f55310ae9ed489c4905ed02cb']='charts.d/apache.conf'
   ['c080e006f544c949baca33cc24a9c126']='health_alarm_notify.conf'
   ['c1a7e634b5b8aad523a0d115a93379cd']='health.d/memcached.conf'
@@ -362,6 +367,7 @@ declare -A configs_signatures=(
   ['e0e96cc47ed61d6492416be5236cd4d3']='python.d/apache_cache.conf'
   ['e2f3388c06726154c10ec22bad5bc7ec']='fping.conf'
   ['e3023092e3b2bbb5351e0fe6682f4fe9']='health_alarm_notify.conf'
+  ['e3d100c2d0347c08efbf6245e05620c6']='python.d/fail2ban.conf'
   ['e3e5bc57335c489f01b8559f5c70e112']='python.d/squid.conf'
   ['e40947d22f7ed5359f12fc89e3512963']='python.d/dovecot.conf'
   ['e449e5582279742496550df14b6fca95']='health.d/entropy.conf'
index 417c5ca2e968dc7a8f096de63b63a8d6117f49bd..51fc25820e54a8e301b5c00ab7f17d8e75a64246 100755 (executable)
@@ -723,7 +723,7 @@ if [ ${UID} -eq 0 ]
             # if we managed to setcap
             # but we fail to execute apps.plugin
             # trigger setuid to root
-            "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin" -v >/dev/null 2>&1
+            "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin" -t >/dev/null 2>&1
             setcap_ret=$?
         fi
     fi
index d0c581654e49aebd6aa611d8087ceecebf2ef120..e9cb3db9e82845a7e86bbdcdfff4b31685cb7024 100644 (file)
@@ -25,6 +25,7 @@ dist_python_SCRIPTS = \
     isc_dhcpd.chart.py \
     mdstat.chart.py \
     memcached.chart.py \
+    mongodb.chart.py \
     mysql.chart.py \
     nginx.chart.py \
     ovpn_status_log.chart.py \
index 5fccf30b8d48007131104de627d82e3764ecb322..d21d2d83ecea730d71aca1e0798b68cf670b1d58 100644 (file)
@@ -710,6 +710,149 @@ If no configuration is given, module will attempt to connect to memcached instan
 
 ---
 
+# mongodb
+
+Module monitor mongodb performance and health metrics
+
+**Requirements:**
+ * `python-pymongo` package.
+
+You need to install it manually.
+
+
+Number of charts depends on mongodb version, storage engine and other features (replication):
+
+1. **Read requests**:
+ * query
+ * getmore (operation the cursor executes to get additional data from query)
+
+2. **Write requests**:
+ * insert
+ * delete
+ * update
+
+3. **Active clients**:
+ * readers (number of clients with read operations in progress or queued)
+ * writers (number of clients with write operations in progress or queued)
+
+4. **Journal transactions**:
+ * commits (count of transactions that have been written to the journal)
+
+5. **Data written to the journal**:
+ * volume (volume of data)
+
+6. **Background flush** (MMAPv1):
+ * average ms (average time taken by flushes to execute)
+ * last ms (time taken by the last flush)
+
+8. **Read tickets** (WiredTiger):
+ * in use (number of read tickets in use)
+ * available (number of available read tickets remaining)
+
+9. **Write tickets** (WiredTiger):
+ * in use (number of write tickets in use)
+ * available (number of available write tickets remaining)
+
+10. **Cursors**:
+ * opened (number of cursors currently opened by MongoDB for clients)
+ * timedOut (number of cursors that have timed)
+ * noTimeout (number of open cursors with timeout disabled)
+
+11. **Connections**:
+ * connected (number of clients currently connected to the database server)
+ * unused (number of unused connections available for new clients)
+
+12. **Memory usage metrics**:
+ * virtual
+ * resident (amount of memory used by the database process)
+ * mapped
+ * non mapped
+
+13. **Page faults**:
+ * page faults (number of times MongoDB had to request from disk)
+
+14. **Cache metrics** (WiredTiger):
+ * percentage of bytes currently in the cache (amount of space taken by cached data)
+ * percantage of tracked dirty bytes in the cache (amount of space taken by dirty data)
+
+15. **Pages evicted from cache** (WiredTiger):
+ * modified
+ * unmodified
+
+16. **Queued requests**:
+ * readers (number of read request currently queued)
+ * writers (number of write request currently queued)
+
+17. **Errors**:
+ * msg (number of message assertions raised)
+ * warning (number of warning assertions raised)
+ * regular (number of regular assertions raised)
+ * user (number of assertions corresponding to errors generated by users)
+
+18. **Storage metrics** (one chart for every database)
+ * dataSize (size of all documents + padding in the database)
+ * indexSize (size of all indexes in the database)
+ * storageSize (size of all extents in the database)
+
+19. **Documents in the database** (one chart for all databases)
+ * documents (number of objects in the database among all the collections)
+
+20. **tcmalloc metrics**
+ * central cache free
+ * current total thread cache
+ * pageheap free
+ * pageheap unmapped
+ * thread cache free
+ * transfer cache free
+ * heap size
+
+21. **Commands total/failed rate**
+ * count
+ * createIndex
+ * delete
+ * eval
+ * findAndModify
+ * insert
+
+22. **Locks metrics** (acquireCount metrics - number of times the lock was acquired in the specified mode)
+ * Global lock
+ * Database lock
+ * Collection lock
+ * Metadata lock
+ * oplog lock
+
+23. **Replica set members state**
+ * state
+
+24. **Oplog window**
+  * window (interval of time between the oldest and the latest entries in the oplog)
+
+25. **Replication lag**
+  * member (time when last entry from the oplog was applied for every member)
+
+26. **Replication set member heartbeat latency**
+  * member (time when last heartbeat was received from replica set member)
+
+
+### configuration
+
+Sample:
+
+```yaml
+local:
+    name : 'local'
+    host : '127.0.0.1'
+    port : 27017
+    user : 'netdata'
+    pass : 'netdata'
+
+```
+
+If no configuration is given, module will attempt to connect to mongodb daemon on `127.0.0.1:27017` address
+
+---
+
+
 # mysql
 
 Module monitors one or more mysql servers
index 15d9538b4e5c24e91daf4d8c24ceebac6ae0d03b..de74a395ad1a3a43bab66fd82a00721843830fef 100644 (file)
@@ -10,6 +10,7 @@ try:
 except ImportError:
         from Queue import Queue
 from threading import Thread
+from collections import namedtuple
 
 # default module values (can be overridden per job in `config`)
 # update_every = 2
@@ -17,6 +18,73 @@ update_every = 5
 priority = 60000
 retries = 60
 
+METHODS = namedtuple('METHODS', ['get_data_function', 'url'])
+
+NODE_STATS = [
+    ('indices.search.fetch_current', None, None),
+    ('indices.search.fetch_total', None, None),
+    ('indices.search.query_current', None, None),
+    ('indices.search.query_total', None, None),
+    ('indices.search.query_time_in_millis', None, None),
+    ('indices.search.fetch_time_in_millis', None, None),
+    ('indices.indexing.index_total', 'indexing_index_total', None),
+    ('indices.indexing.index_current', 'indexing_index_current', None),
+    ('indices.indexing.index_time_in_millis', 'indexing_index_time_in_millis', None),
+    ('indices.refresh.total', 'refresh_total', None),
+    ('indices.refresh.total_time_in_millis', 'refresh_total_time_in_millis', None),
+    ('indices.flush.total', 'flush_total', None),
+    ('indices.flush.total_time_in_millis', 'flush_total_time_in_millis', None),
+    ('jvm.gc.collectors.young.collection_count', 'young_collection_count', None),
+    ('jvm.gc.collectors.old.collection_count', 'old_collection_count', None),
+    ('jvm.gc.collectors.young.collection_time_in_millis', 'young_collection_time_in_millis', None),
+    ('jvm.gc.collectors.old.collection_time_in_millis', 'old_collection_time_in_millis', None),
+    ('jvm.mem.heap_used_percent', 'jvm_heap_percent', None),
+    ('jvm.mem.heap_committed_in_bytes', 'jvm_heap_commit', None),
+    ('thread_pool.bulk.queue', 'bulk_queue', None),
+    ('thread_pool.bulk.rejected', 'bulk_rejected', None),
+    ('thread_pool.index.queue', 'index_queue', None),
+    ('thread_pool.index.rejected', 'index_rejected', None),
+    ('thread_pool.search.queue', 'search_queue', None),
+    ('thread_pool.search.rejected', 'search_rejected', None),
+    ('thread_pool.merge.queue', 'merge_queue', None),
+    ('thread_pool.merge.rejected', 'merge_rejected', None),
+    ('indices.fielddata.memory_size_in_bytes', 'index_fdata_memory', None),
+    ('indices.fielddata.evictions', None, None),
+    ('breakers.fielddata.tripped', None, None),
+    ('http.current_open', 'http_current_open', None),
+    ('transport.rx_size_in_bytes', 'transport_rx_size_in_bytes', None),
+    ('transport.tx_size_in_bytes', 'transport_tx_size_in_bytes', None),
+    ('process.max_file_descriptors', None, None),
+    ('process.open_file_descriptors', None, None)
+]
+
+CLUSTER_STATS = [
+    ('nodes.count.data_only', 'count_data_only', None),
+    ('nodes.count.master_data', 'count_master_data', None),
+    ('nodes.count.total', 'count_total', None),
+    ('nodes.count.master_only', 'count_master_only', None),
+    ('nodes.count.client', 'count_client', None),
+    ('indices.docs.count', 'docs_count', None),
+    ('indices.query_cache.hit_count', 'query_cache_hit_count', None),
+    ('indices.query_cache.miss_count', 'query_cache_miss_count', None),
+    ('indices.store.size_in_bytes', 'store_size_in_bytes', None),
+    ('indices.count', 'indices_count', None),
+    ('indices.shards.total', 'shards_total', None)
+]
+
+HEALTH_STATS = [
+    ('number_of_nodes', 'health_number_of_nodes', None),
+    ('number_of_data_nodes', 'health_number_of_data_nodes', None),
+    ('number_of_pending_tasks', 'health_number_of_pending_tasks', None),
+    ('number_of_in_flight_fetch', 'health_number_of_in_flight_fetch', None),
+    ('active_shards', 'health_active_shards', None),
+    ('relocating_shards', 'health_relocating_shards', None),
+    ('unassigned_shards', 'health_unassigned_shards', None),
+    ('delayed_unassigned_shards', 'health_delayed_unassigned_shards', None),
+    ('initializing_shards', 'health_initializing_shards', None),
+    ('active_shards_percent_as_number', 'health_active_shards_percent_as_number', None)
+]
+
 # charts order (can be overridden if you want less charts, or different order)
 ORDER = ['search_perf_total', 'search_perf_current', 'search_perf_time', 'search_latency', 'index_perf_total',
          'index_perf_current', 'index_perf_time', 'index_latency', 'jvm_mem_heap', 'jvm_gc_count',
@@ -27,19 +95,22 @@ ORDER = ['search_perf_total', 'search_perf_current', 'search_perf_time', 'search
 
 CHARTS = {
     'search_perf_total': {
-        'options': [None, 'Total number of queries, fetches', 'number of', 'search performance', 'es.search_query_total', 'stacked'],
+        'options': [None, 'Total number of queries, fetches', 'number of', 'search performance',
+                    'es.search_query_total', 'stacked'],
         'lines': [
             ['query_total', 'queries', 'incremental'],
             ['fetch_total', 'fetches', 'incremental']
         ]},
     'search_perf_current': {
-        'options': [None, 'Number of queries, fetches in progress', 'number of', 'search performance', 'es.search_query_current', 'stacked'],
+        'options': [None, 'Number of queries, fetches in progress', 'number of', 'search performance',
+                    'es.search_query_current', 'stacked'],
         'lines': [
             ['query_current', 'queries', 'absolute'],
             ['fetch_current', 'fetches', 'absolute']
         ]},
     'search_perf_time': {
-        'options': [None, 'Time spent on queries, fetches', 'seconds', 'search performance', 'es.search_time', 'stacked'],
+        'options': [None, 'Time spent on queries, fetches', 'seconds', 'search performance',
+                    'es.search_time', 'stacked'],
         'lines': [
             ['query_time_in_millis', 'query', 'incremental', 1, 1000],
             ['fetch_time_in_millis', 'fetch', 'incremental', 1, 1000]
@@ -100,7 +171,7 @@ CHARTS = {
         ]},
     'thread_pool_qr_q': {
         'options': [None, 'Number of queued threads in thread pool', 'queued threads', 'queues and rejections',
-                    'es.qr_queued', 'stacked'],
+                    'es.thread_pool_queued', 'stacked'],
         'lines': [
             ['bulk_queue', 'bulk', 'absolute'],
             ['index_queue', 'index', 'absolute'],
@@ -109,7 +180,7 @@ CHARTS = {
         ]},
     'thread_pool_qr_r': {
         'options': [None, 'Number of rejected threads in thread pool', 'rejected threads', 'queues and rejections',
-                    'es.qr_rejected', 'stacked'],
+                    'es.thread_pool_rejected', 'stacked'],
         'lines': [
             ['bulk_rejected', 'bulk', 'absolute'],
             ['index_rejected', 'index', 'absolute'],
@@ -119,23 +190,23 @@ CHARTS = {
     'fdata_cache': {
         'options': [None, 'Fielddata cache size', 'MB', 'fielddata cache', 'es.fdata_cache', 'line'],
         'lines': [
-            ['index_fdata_mem', 'mem_size', 'absolute', 1, 1048576]
+            ['index_fdata_memory', 'cache', 'absolute', 1, 1048576]
         ]},
     'fdata_ev_tr': {
         'options': [None, 'Fielddata evictions and circuit breaker tripped count', 'number of events',
-                    'fielddata cache', 'es.fdata_ev_tr', 'line'],
+                    'fielddata cache', 'es.evictions_tripped', 'line'],
         'lines': [
-            ['index_fdata_evic', 'evictions', 'incremental'],
-            ['breakers_fdata_trip', 'tripped', 'incremental']
+            ['evictions', None, 'incremental'],
+            ['tripped', None, 'incremental']
         ]},
     'cluster_health_nodes': {
         'options': [None, 'Nodes and tasks statistics', 'units', 'cluster health API',
-                    'es.cluster_health', 'stacked'],
+                    'es.cluster_health_nodes', 'stacked'],
         'lines': [
             ['health_number_of_nodes', 'nodes', 'absolute'],
             ['health_number_of_data_nodes', 'data_nodes', 'absolute'],
             ['health_number_of_pending_tasks', 'pending_tasks', 'absolute'],
-            ['health_number_of_in_flight_fetch', 'inflight_fetch', 'absolute']
+            ['health_number_of_in_flight_fetch', 'in_flight_fetch', 'absolute']
         ]},
     'cluster_health_status': {
         'options': [None, 'Cluster status', 'status', 'cluster health API',
@@ -150,7 +221,7 @@ CHARTS = {
         ]},
     'cluster_health_shards': {
         'options': [None, 'Shards statistics', 'shards', 'cluster health API',
-                    'es.cluster_health_sharts', 'stacked'],
+                    'es.cluster_health_shards', 'stacked'],
         'lines': [
             ['health_active_shards', 'active_shards', 'absolute'],
             ['health_relocating_shards', 'relocating_shards', 'absolute'],
@@ -161,7 +232,7 @@ CHARTS = {
         ]},
     'cluster_stats_nodes': {
         'options': [None, 'Nodes statistics', 'nodes', 'cluster stats API',
-                    'es.cluster_stats_nodes', 'stacked'],
+                    'es.cluster_nodes', 'stacked'],
         'lines': [
             ['count_data_only', 'data_only', 'absolute'],
             ['count_master_data', 'master_data', 'absolute'],
@@ -171,46 +242,46 @@ CHARTS = {
         ]},
     'cluster_stats_query_cache': {
         'options': [None, 'Query cache statistics', 'queries', 'cluster stats API',
-                    'es.cluster_stats_query_cache', 'stacked'],
+                    'es.cluster_query_cache', 'stacked'],
         'lines': [
             ['query_cache_hit_count', 'hit', 'incremental'],
             ['query_cache_miss_count', 'miss', 'incremental']
         ]},
     'cluster_stats_docs': {
         'options': [None, 'Docs statistics', 'count', 'cluster stats API',
-                    'es.cluster_stats_docs', 'line'],
+                    'es.cluster_docs', 'line'],
         'lines': [
             ['docs_count', 'docs', 'absolute']
         ]},
     'cluster_stats_store': {
         'options': [None, 'Store statistics', 'MB', 'cluster stats API',
-                    'es.cluster_stats_store', 'line'],
+                    'es.cluster_store', 'line'],
         'lines': [
             ['store_size_in_bytes', 'size', 'absolute', 1, 1048567]
         ]},
     'cluster_stats_indices_shards': {
         'options': [None, 'Indices and shards statistics', 'count', 'cluster stats API',
-                    'es.cluster_stats_ind_sha', 'stacked'],
+                    'es.cluster_indices_shards', 'stacked'],
         'lines': [
             ['indices_count', 'indices', 'absolute'],
             ['shards_total', 'shards', 'absolute']
         ]},
     'host_metrics_transport': {
         'options': [None, 'Cluster communication transport metrics', 'kbit/s', 'host metrics',
-                    'es.host_metrics_transport', 'area'],
+                    'es.host_transport', 'area'],
         'lines': [
             ['transport_rx_size_in_bytes', 'in', 'incremental', 8, 1000],
             ['transport_tx_size_in_bytes', 'out', 'incremental', -8, 1000]
         ]},
     'host_metrics_file_descriptors': {
         'options': [None, 'Available file descriptors in percent', 'percent', 'host metrics',
-                    'es.host_metrics_descriptors', 'area'],
+                    'es.host_descriptors', 'area'],
         'lines': [
             ['file_descriptors_used', 'used', 'absolute', 1, 10]
         ]},
     'host_metrics_http': {
         'options': [None, 'Opened HTTP connections', 'connections', 'host metrics',
-                    'es.host_metrics_http', 'line'],
+                    'es.host_http_connections', 'line'],
         'lines': [
             ['http_current_open', 'opened', 'absolute', 1, 1]
         ]}
@@ -227,6 +298,8 @@ class Service(UrlService):
         self.user = self.configuration.get('user')
         self.password = self.configuration.get('pass')
         self.latency = dict()
+        self.methods = list()
+        self.auth = self.user and self.password
 
     def check(self):
         # We can't start if <host> AND <port> not specified
@@ -234,16 +307,13 @@ class Service(UrlService):
             return False
 
         # It as a bad idea to use hostname.
-        # Hostname -> ipaddress
+        # Hostname -> ip address
         try:
             self.host = gethostbyname(self.host)
-        except Exception as e:
-            self.error(str(e))
+        except Exception as error:
+            self.error(str(error))
             return False
 
-        # HTTP Auth? NOT TESTED
-        self.auth = self.user and self.password
-
         # Create URL for every Elasticsearch API
         url_node_stats = 'http://%s:%s/_nodes/_local/stats' % (self.host, self.port)
         url_cluster_health = 'http://%s:%s/_cluster/health' % (self.host, self.port)
@@ -253,26 +323,30 @@ class Service(UrlService):
         user_choice = [bool(self.configuration.get('node_stats', True)),
                        bool(self.configuration.get('cluster_health', True)),
                        bool(self.configuration.get('cluster_stats', True))]
-        
-        avail_methods = [(self._get_node_stats, url_node_stats), 
-                        (self._get_cluster_health, url_cluster_health),
-                        (self._get_cluster_stats, url_cluster_stats)]
+
+        avail_methods = [METHODS(get_data_function=self._get_node_stats_, url=url_node_stats),
+                         METHODS(get_data_function=self._get_cluster_health_, url=url_cluster_health),
+                         METHODS(get_data_function=self._get_cluster_stats_, url=url_cluster_stats)]
 
         # Remove disabled API calls from 'avail methods'
         self.methods = [avail_methods[_] for _ in range(len(avail_methods)) if user_choice[_]]
 
         # Run _get_data for ALL active API calls. 
-        api_result = {}
+        api_check_result = dict()
         for method in self.methods:
-            api_result[method[1]] = (bool(self._get_raw_data(method[1])))
+            try:
+                api_check_result[method.url] = (bool(method.get_data_function(None, method.url)))
+            except KeyError as error:
+                self.error('Failed to parse %s. Error: %s'  % (method.url, str(error)))
+                return False
 
         # We can start ONLY if all active API calls returned NOT None
-        if not all(api_result.values()):
+        if not all(api_check_result.values()):
             self.error('Plugin could not get data from all APIs')
-            self.error('%s' % api_result)
+            self.error('%s' % api_check_result)
             return False
         else:
-            self.info('%s' % api_result)
+            self.info('%s' % api_check_result)
             self.info('Plugin was started successfully')
 
             return True
@@ -294,7 +368,7 @@ class Service(UrlService):
         result = dict()
 
         for method in self.methods:
-            th = Thread(target=method[0], args=(queue, method[1]))
+            th = Thread(target=method.get_data_function, args=(queue, method.url))
             th.start()
             threads.append(th)
 
@@ -304,7 +378,7 @@ class Service(UrlService):
 
         return result or None
 
-    def _get_cluster_health(self, queue, url):
+    def _get_cluster_health_(self, queue, url):
         """
         Format data received from http request
         :return: dict
@@ -313,19 +387,20 @@ class Service(UrlService):
         data = self._get_raw_data(url)
 
         if not data:
-            queue.put({})
+            return queue.put(dict()) if queue else None
         else:
-            data = data.json() if '__call__' in dir(data.json) else data.json
+            data = data.json() if hasattr(data.json, '__call__') else data.json
+
+            to_netdata = fetch_data_(raw_data=data, metrics_list=HEALTH_STATS)
 
-            to_netdata = dict()
-            to_netdata.update(update_key('health', data))
             to_netdata.update({'status_green': 0, 'status_red': 0, 'status_yellow': 0,
                                'status_foo1': 0, 'status_foo2': 0, 'status_foo3': 0})
-            to_netdata[''.join(['status_', to_netdata.get('health_status', '')])] = 1
+            current_status = 'status_' + data['status']
+            to_netdata[current_status] = 1
 
-            queue.put(to_netdata)
+            return queue.put(to_netdata) if queue else to_netdata
 
-    def _get_cluster_stats(self, queue, url):
+    def _get_cluster_stats_(self, queue, url):
         """
         Format data received from http request
         :return: dict
@@ -334,21 +409,15 @@ class Service(UrlService):
         data = self._get_raw_data(url)
 
         if not data:
-            queue.put({})
+            return queue.put(dict()) if queue else None
         else:
-            data = data.json() if '__call__' in dir(data.json) else data.json
+            data = data.json() if hasattr(data.json, '__call__') else data.json
 
-            to_netdata = dict()
-            to_netdata.update(update_key('count', data['nodes']['count']))
-            to_netdata.update(update_key('query_cache', data['indices']['query_cache']))
-            to_netdata.update(update_key('docs', data['indices']['docs']))
-            to_netdata.update(update_key('store', data['indices']['store']))
-            to_netdata['indices_count'] = data['indices']['count']
-            to_netdata['shards_total'] = data['indices'].get('shards', {}).get('total')
+            to_netdata = fetch_data_(raw_data=data, metrics_list=CLUSTER_STATS)
 
-            queue.put(to_netdata)
+            return queue.put(to_netdata) if queue else to_netdata
 
-    def _get_node_stats(self, queue, url):
+    def _get_node_stats_(self, queue, url):
         """
         Format data received from http request
         :return: dict
@@ -357,50 +426,31 @@ class Service(UrlService):
         data = self._get_raw_data(url)
 
         if not data:
-            queue.put({})
+            return queue.put(dict()) if queue else None
         else:
-            data = data.json() if '__call__' in dir(data.json) else data.json
+            data = data.json() if hasattr(data.json, '__call__') else data.json
 
             node = list(data['nodes'].keys())[0]
-            to_netdata = dict()
-            # Search performance metrics
-            to_netdata.update(data['nodes'][node]['indices']['search'])
-            to_netdata['query_latency'] = self.find_avg(to_netdata['query_total'],
-                                               to_netdata['query_time_in_millis'], 'query_latency')
-            to_netdata['fetch_latency'] = self.find_avg(to_netdata['fetch_total'],
-                                               to_netdata['fetch_time_in_millis'], 'fetch_latency')
-
-            # Indexing performance metrics
-            for key in ['indexing', 'refresh', 'flush']:
-                to_netdata.update(update_key(key, data['nodes'][node]['indices'].get(key, {})))
-            to_netdata['indexing_latency'] = self.find_avg(to_netdata['indexing_index_total'],
-                                               to_netdata['indexing_index_time_in_millis'], 'index_latency')
-            to_netdata['flushing_latency'] = self.find_avg(to_netdata['flush_total'],
-                                               to_netdata['flush_total_time_in_millis'], 'flush_latency')
-            # Memory usage and garbage collection
-            to_netdata.update(update_key('young', data['nodes'][node]['jvm']['gc']['collectors']['young']))
-            to_netdata.update(update_key('old', data['nodes'][node]['jvm']['gc']['collectors']['old']))
-            to_netdata['jvm_heap_percent'] = data['nodes'][node]['jvm']['mem']['heap_used_percent']
-            to_netdata['jvm_heap_commit'] = data['nodes'][node]['jvm']['mem']['heap_committed_in_bytes']
-
-            # Thread pool queues and rejections
-            for key in ['bulk', 'index', 'search', 'merge']:
-                to_netdata.update(update_key(key, data['nodes'][node]['thread_pool'].get(key, {})))
-
-            # Fielddata cache
-            to_netdata['index_fdata_mem'] = data['nodes'][node]['indices']['fielddata']['memory_size_in_bytes']
-            to_netdata['index_fdata_evic'] = data['nodes'][node]['indices']['fielddata']['evictions']
-            to_netdata['breakers_fdata_trip'] = data['nodes'][node]['breakers']['fielddata']['tripped']
-
-            # Host metrics
-            to_netdata.update(update_key('http', data['nodes'][node]['http']))
-            to_netdata.update(update_key('transport', data['nodes'][node]['transport']))
-            to_netdata['file_descriptors_used'] = round(float(data['nodes'][node]['process']['open_file_descriptors'])
-                                                        / data['nodes'][node]['process']['max_file_descriptors'] * 1000)
-            
-            queue.put(to_netdata)
-
-    def find_avg(self, value1, value2, key):
+            to_netdata = fetch_data_(raw_data=data['nodes'][node], metrics_list=NODE_STATS)
+
+            # Search performance latency
+            to_netdata['query_latency'] = self.find_avg_(to_netdata['query_total'],
+                                                         to_netdata['query_time_in_millis'], 'query_latency')
+            to_netdata['fetch_latency'] = self.find_avg_(to_netdata['fetch_total'],
+                                                         to_netdata['fetch_time_in_millis'], 'fetch_latency')
+
+            # Indexing performance latency
+            to_netdata['indexing_latency'] = self.find_avg_(to_netdata['indexing_index_total'],
+                                                            to_netdata['indexing_index_time_in_millis'], 'index_latency')
+            to_netdata['flushing_latency'] = self.find_avg_(to_netdata['flush_total'],
+                                                            to_netdata['flush_total_time_in_millis'], 'flush_latency')
+
+            to_netdata['file_descriptors_used'] = round(float(to_netdata['open_file_descriptors'])
+                                                        / to_netdata['max_file_descriptors'] * 1000)
+
+            return queue.put(to_netdata) if queue else to_netdata
+
+    def find_avg_(self, value1, value2, key):
         if key not in self.latency:
             self.latency.update({key: [value1, value2]})
             return 0
@@ -414,5 +464,17 @@ class Service(UrlService):
                 return 0
 
 
-def update_key(string, dictionary):
-    return dict([('_'.join([string, elem[0]]), elem[1]) for elem in dictionary.items()])
+def fetch_data_(raw_data, metrics_list):
+    to_netdata = dict()
+    for metric, new_name, function in metrics_list:
+        value = raw_data
+        for key in metric.split('.'):
+            try:
+                value = value[key]
+            except KeyError:
+                break
+        if not isinstance(value, dict) and key:
+            to_netdata[new_name or key] = value if not function else function(value)
+
+    return to_netdata
+
diff --git a/python.d/mongodb.chart.py b/python.d/mongodb.chart.py
new file mode 100644 (file)
index 0000000..c01bd29
--- /dev/null
@@ -0,0 +1,672 @@
+# -*- coding: utf-8 -*-
+# Description: mongodb netdata python.d module
+# Author: l2isbad
+
+from base import SimpleService
+from copy import deepcopy
+from datetime import datetime
+from sys import exc_info
+
+try:
+    from pymongo import MongoClient, ASCENDING, DESCENDING
+    from pymongo.errors import PyMongoError
+    PYMONGO = True
+except ImportError:
+    PYMONGO = False
+
+# default module values (can be overridden per job in `config`)
+# update_every = 2
+priority = 60000
+retries = 60
+
+REPLSET_STATES = [
+    ('1', 'primary'),
+    ('8', 'down'),
+    ('2', 'secondary'),
+    ('3', 'recovering'),
+    ('5', 'startup2'),
+    ('4', 'fatal'),
+    ('7', 'arbiter'),
+    ('6', 'unknown'),
+    ('9', 'rollback'),
+    ('10', 'removed'),
+    ('0', 'startup')]
+
+
+def multiply_by_100(value):
+    return value * 100
+
+DEFAULT_METRICS = [
+    ('opcounters.delete', None, None),
+    ('opcounters.update', None, None),
+    ('opcounters.insert', None, None),
+    ('opcounters.query', None, None),
+    ('opcounters.getmore', None, None),
+    ('globalLock.activeClients.readers', 'activeClients_readers', None),
+    ('globalLock.activeClients.writers', 'activeClients_writers', None),
+    ('connections.available', 'connections_available', None),
+    ('connections.current', 'connections_current', None),
+    ('mem.mapped', None, None),
+    ('mem.resident', None, None),
+    ('mem.virtual', None, None),
+    ('globalLock.currentQueue.readers', 'currentQueue_readers', None),
+    ('globalLock.currentQueue.writers', 'currentQueue_writers', None),
+    ('asserts.msg', None, None),
+    ('asserts.regular', None, None),
+    ('asserts.user', None, None),
+    ('asserts.warning', None, None),
+    ('extra_info.page_faults', None, None),
+    ('metrics.record.moves', None, None),
+    ('backgroundFlushing.average_ms', None, multiply_by_100),
+    ('backgroundFlushing.last_ms', None, multiply_by_100),
+    ('backgroundFlushing.flushes', None, multiply_by_100),
+    ('metrics.cursor.timedOut', None, None),
+    ('metrics.cursor.open.total', 'cursor_total', None),
+    ('metrics.cursor.open.noTimeout', None, None),
+    ('cursors.timedOut', None, None),
+    ('cursors.totalOpen', 'cursor_total', None)
+]
+
+DUR = [
+    ('dur.commits', None, None),
+    ('dur.journaledMB', None, multiply_by_100)
+]
+
+WIREDTIGER = [
+    ('wiredTiger.concurrentTransactions.read.available', 'wiredTigerRead_available', None),
+    ('wiredTiger.concurrentTransactions.read.out', 'wiredTigerRead_out', None),
+    ('wiredTiger.concurrentTransactions.write.available', 'wiredTigerWrite_available', None),
+    ('wiredTiger.concurrentTransactions.write.out', 'wiredTigerWrite_out', None),
+    ('wiredTiger.cache.bytes currently in the cache', None, None),
+    ('wiredTiger.cache.tracked dirty bytes in the cache', None, None),
+    ('wiredTiger.cache.maximum bytes configured', None, None),
+    ('wiredTiger.cache.unmodified pages evicted', 'unmodified', None),
+    ('wiredTiger.cache.modified pages evicted', 'modified', None)
+]
+
+TCMALLOC = [
+    ('tcmalloc.generic.current_allocated_bytes', None, None),
+    ('tcmalloc.generic.heap_size', None, None),
+    ('tcmalloc.tcmalloc.central_cache_free_bytes', None, None),
+    ('tcmalloc.tcmalloc.current_total_thread_cache_bytes', None, None),
+    ('tcmalloc.tcmalloc.pageheap_free_bytes', None, None),
+    ('tcmalloc.tcmalloc.pageheap_unmapped_bytes', None, None),
+    ('tcmalloc.tcmalloc.thread_cache_free_bytes', None, None),
+    ('tcmalloc.tcmalloc.transfer_cache_free_bytes', None, None)
+]
+
+COMMANDS = [
+    ('metrics.commands.count.total', 'count_total', None),
+    ('metrics.commands.createIndexes.total', 'createIndexes_total', None),
+    ('metrics.commands.delete.total', 'delete_total', None),
+    ('metrics.commands.eval.total', 'eval_total', None),
+    ('metrics.commands.findAndModify.total', 'findAndModify_total', None),
+    ('metrics.commands.insert.total', 'insert_total', None),
+    ('metrics.commands.delete.total', 'delete_total', None),
+    ('metrics.commands.count.failed', 'count_failed', None),
+    ('metrics.commands.createIndexes.failed', 'createIndexes_failed', None),
+    ('metrics.commands.delete.failed', 'delete_failed', None),
+    ('metrics.commands.eval.failed', 'eval_failed', None),
+    ('metrics.commands.findAndModify.failed', 'findAndModify_failed', None),
+    ('metrics.commands.insert.failed', 'insert_failed', None),
+    ('metrics.commands.delete.failed', 'delete_failed', None)
+]
+
+LOCKS = [
+    ('locks.Collection.acquireCount.R', 'Collection_R', None),
+    ('locks.Collection.acquireCount.r', 'Collection_r', None),
+    ('locks.Collection.acquireCount.W', 'Collection_W', None),
+    ('locks.Collection.acquireCount.w', 'Collection_w', None),
+    ('locks.Database.acquireCount.R', 'Database_R', None),
+    ('locks.Database.acquireCount.r', 'Database_r', None),
+    ('locks.Database.acquireCount.W', 'Database_W', None),
+    ('locks.Database.acquireCount.w', 'Database_w', None),
+    ('locks.Global.acquireCount.R', 'Global_R', None),
+    ('locks.Global.acquireCount.r', 'Global_r', None),
+    ('locks.Global.acquireCount.W', 'Global_W', None),
+    ('locks.Global.acquireCount.w', 'Global_w', None),
+    ('locks.Metadata.acquireCount.R', 'Metadata_R', None),
+    ('locks.Metadata.acquireCount.w', 'Metadata_w', None),
+    ('locks.oplog.acquireCount.r', 'oplog_r', None),
+    ('locks.oplog.acquireCount.w', 'oplog_w', None)
+]
+
+DBSTATS = [
+    'dataSize',
+    'indexSize',
+    'storageSize',
+    'objects'
+]
+
+# charts order (can be overridden if you want less charts, or different order)
+ORDER = ['read_operations', 'write_operations', 'active_clients', 'journaling_transactions',
+         'journaling_volume', 'background_flush_average', 'background_flush_last', 'background_flush_rate',
+         'wiredtiger_read', 'wiredtiger_write', 'cursors', 'connections', 'memory', 'page_faults',
+         'queued_requests', 'record_moves', 'wiredtiger_cache', 'wiredtiger_pages_evicted', 'asserts',
+         'locks_collection', 'locks_database', 'locks_global', 'locks_metadata', 'locks_oplog',
+         'dbstats_objects', 'tcmalloc_generic', 'tcmalloc_metrics', 'command_total_rate', 'command_failed_rate']
+
+CHARTS = {
+    'read_operations': {
+        'options': [None, 'Received read requests', 'requests/s', 'throughput metrics',
+                    'mongodb.read_operations', 'line'],
+        'lines': [
+            ['query', None, 'incremental'],
+            ['getmore', None, 'incremental']
+        ]},
+    'write_operations': {
+        'options': [None, 'Received write requests', 'requests/s', 'throughput metrics',
+                    'mongodb.write_operations', 'line'],
+        'lines': [
+            ['insert', None, 'incremental'],
+            ['update', None, 'incremental'],
+            ['delete', None, 'incremental']
+        ]},
+    'active_clients': {
+        'options': [None, 'Clients with read or write operations in progress or queued', 'clients',
+                    'throughput metrics', 'mongodb.active_clients', 'line'],
+        'lines': [
+            ['activeClients_readers', 'readers', 'absolute'],
+            ['activeClients_writers', 'writers', 'absolute']
+        ]},
+    'journaling_transactions': {
+        'options': [None, 'Transactions that have been written to the journal', 'commits',
+                    'database performance', 'mongodb.journaling_transactions', 'line'],
+        'lines': [
+            ['commits', None, 'absolute']
+        ]},
+    'journaling_volume': {
+        'options': [None, 'Volume of data written to the journal', 'MB', 'database performance',
+                    'mongodb.journaling_volume', 'line'],
+        'lines': [
+            ['journaledMB', 'volume', 'absolute', 1, 100]
+        ]},
+    'background_flush_average': {
+        'options': [None, 'Average time taken by flushes to execute', 'ms', 'database performance',
+                    'mongodb.background_flush_average', 'line'],
+        'lines': [
+            ['average_ms', 'time', 'absolute', 1, 100]
+        ]},
+    'background_flush_last': {
+        'options': [None, 'Time taken by the last flush operation to execute', 'ms', 'database performance',
+                    'mongodb.background_flush_last', 'line'],
+        'lines': [
+            ['last_ms', 'time', 'absolute', 1, 100]
+        ]},
+    'background_flush_rate': {
+        'options': [None, 'Flushes rate', 'flushes', 'database performance', 'mongodb.background_flush_rate', 'line'],
+        'lines': [
+            ['flushes', 'flushes', 'incremental', 1, 1]
+        ]},
+    'wiredtiger_read': {
+        'options': [None, 'Read tickets in use and remaining', 'tickets', 'database performance',
+                    'mongodb.wiredtiger_read', 'stacked'],
+        'lines': [
+            ['wiredTigerRead_available', 'available', 'absolute', 1, 1],
+            ['wiredTigerRead_out', 'inuse', 'absolute', 1, 1]
+        ]},
+    'wiredtiger_write': {
+        'options': [None, 'Write tickets in use and remaining', 'tickets', 'database performance',
+                    'mongodb.wiredtiger_write', 'stacked'],
+        'lines': [
+            ['wiredTigerWrite_available', 'available', 'absolute', 1, 1],
+            ['wiredTigerWrite_out', 'inuse', 'absolute', 1, 1]
+        ]},
+    'cursors': {
+        'options': [None, 'Currently openned cursors, cursors with timeout disabled and timed out cursors',
+                    'cursors', 'database performance', 'mongodb.cursors', 'stacked'],
+        'lines': [
+            ['cursor_total', 'openned', 'absolute', 1, 1],
+            ['noTimeout', None, 'absolute', 1, 1],
+            ['timedOut', None, 'incremental', 1, 1]
+        ]},
+    'connections': {
+        'options': [None, 'Currently connected clients and unused connections', 'connections',
+                    'resource utilization', 'mongodb.connections', 'stacked'],
+        'lines': [
+            ['connections_available', 'unused', 'absolute', 1, 1],
+            ['connections_current', 'connected', 'absolute', 1, 1]
+        ]},
+    'memory': {
+        'options': [None, 'Memory metrics', 'MB', 'resource utilization', 'mongodb.memory', 'stacked'],
+        'lines': [
+            ['virtual', None, 'absolute', 1, 1],
+            ['resident', None, 'absolute', 1, 1],
+            ['nonmapped', None, 'absolute', 1, 1],
+            ['mapped', None, 'absolute', 1, 1]
+        ]},
+    'page_faults': {
+        'options': [None, 'Number of times MongoDB had to fetch data from disk', 'request/s',
+                    'resource utilization', 'mongodb.page_faults', 'line'],
+        'lines': [
+            ['page_faults', None, 'incremental', 1, 1]
+        ]},
+    'queued_requests': {
+        'options': [None, 'Currently queued read and wrire requests', 'requests', 'resource saturation',
+                    'mongodb.queued_requests', 'line'],
+        'lines': [
+            ['currentQueue_readers', 'readers', 'absolute', 1, 1],
+            ['currentQueue_writers', 'writers', 'absolute', 1, 1]
+        ]},
+    'record_moves': {
+        'options': [None, 'Number of times documents had to be moved on-disk', 'number',
+                    'resource saturation', 'mongodb.record_moves', 'line'],
+        'lines': [
+            ['moves', None, 'incremental', 1, 1]
+        ]},
+    'asserts': {
+        'options': [None, 'Number of message, warning, regular, corresponding to errors generated'
+                          ' by users assertions raised', 'number', 'errors (asserts)', 'mongodb.asserts', 'line'],
+        'lines': [
+            ['msg', None, 'incremental', 1, 1],
+            ['warning', None, 'incremental', 1, 1],
+            ['regular', None, 'incremental', 1, 1],
+            ['user', None, 'incremental', 1, 1]
+        ]},
+    'wiredtiger_cache': {
+        'options': [None, 'The percentage of the wiredTiger cache that is in use and cache with dirty bytes',
+                    'percent', 'resource utilization', 'mongodb.wiredtiger_cache', 'stacked'],
+        'lines': [
+            ['wiredTiger_percent_clean', 'inuse', 'absolute', 1, 1000],
+            ['wiredTiger_percent_dirty', 'dirty', 'absolute', 1, 1000]
+        ]},
+    'wiredtiger_pages_evicted': {
+        'options': [None, 'Pages evicted from the cache',
+                    'pages', 'resource utilization', 'mongodb.wiredtiger_pages_evicted', 'stacked'],
+        'lines': [
+            ['unmodified', None, 'absolute', 1, 1],
+            ['modified', None, 'absolute', 1, 1]
+        ]},
+    'dbstats_objects': {
+        'options': [None, 'Number of documents in the database among all the collections', 'documents',
+                    'storage size metrics', 'mongodb.dbstats_objects', 'stacked'],
+        'lines': [
+        ]},
+    'tcmalloc_generic': {
+        'options': [None, 'Tcmalloc generic metrics', 'MB', 'tcmalloc', 'mongodb.tcmalloc_generic', 'stacked'],
+        'lines': [
+            ['current_allocated_bytes', 'allocated', 'absolute', 1, 1048576],
+            ['heap_size', 'heap_size', 'absolute', 1, 1048576]
+        ]},
+    'tcmalloc_metrics': {
+        'options': [None, 'Tcmalloc metrics', 'KB', 'tcmalloc', 'mongodb.tcmalloc_metrics', 'stacked'],
+        'lines': [
+            ['central_cache_free_bytes', 'central_cache_free', 'absolute', 1, 1024],
+            ['current_total_thread_cache_bytes', 'current_total_thread_cache', 'absolute', 1, 1024],
+            ['pageheap_free_bytes', 'pageheap_free', 'absolute', 1, 1024],
+            ['pageheap_unmapped_bytes', 'pageheap_unmapped', 'absolute', 1, 1024],
+            ['thread_cache_free_bytes', 'thread_cache_free', 'absolute', 1, 1024],
+            ['transfer_cache_free_bytes', 'transfer_cache_free', 'absolute', 1, 1024]
+        ]},
+    'command_total_rate': {
+        'options': [None, 'Commands total rate', 'commands/s', 'commands', 'mongodb.command_total_rate', 'stacked'],
+        'lines': [
+            ['count_total', 'count', 'incremental', 1, 1],
+            ['createIndexes_total', 'createIndexes', 'incremental', 1, 1],
+            ['delete_total', 'delete', 'incremental', 1, 1],
+            ['eval_total', 'eval', 'incremental', 1, 1],
+            ['findAndModify_total', 'findAndModify', 'incremental', 1, 1],
+            ['insert_total', 'insert', 'incremental', 1, 1],
+            ['update_total', 'update', 'incremental', 1, 1]
+        ]},
+    'command_failed_rate': {
+        'options': [None, 'Commands failed rate', 'commands/s', 'commands', 'mongodb.command_failed_rate', 'stacked'],
+        'lines': [
+            ['count_failed', 'count', 'incremental', 1, 1],
+            ['createIndexes_failed', 'createIndexes', 'incremental', 1, 1],
+            ['delete_failed', 'delete', 'incremental', 1, 1],
+            ['eval_failed', 'eval', 'incremental', 1, 1],
+            ['findAndModify_failed', 'findAndModify', 'incremental', 1, 1],
+            ['insert_failed', 'insert', 'incremental', 1, 1],
+            ['update_failed', 'update', 'incremental', 1, 1]
+        ]},
+    'locks_collection': {
+        'options': [None, 'Collection lock. Number of times the lock was acquired in the specified mode',
+                    'locks', 'locks metrics', 'mongodb.locks_collection', 'stacked'],
+        'lines': [
+            ['Collection_R', 'shared', 'incremental'],
+            ['Collection_W', 'exclusive', 'incremental'],
+            ['Collection_r', 'intent_shared', 'incremental'],
+            ['Collection_w', 'intent_exclusive', 'incremental']
+        ]},
+    'locks_database': {
+        'options': [None, 'Database lock. Number of times the lock was acquired in the specified mode',
+                    'locks', 'locks metrics', 'mongodb.locks_database', 'stacked'],
+        'lines': [
+            ['Database_R', 'shared', 'incremental'],
+            ['Database_W', 'exclusive', 'incremental'],
+            ['Database_r', 'intent_shared', 'incremental'],
+            ['Database_w', 'intent_exclusive', 'incremental']
+        ]},
+    'locks_global': {
+        'options': [None, 'Global lock. Number of times the lock was acquired in the specified mode',
+                    'locks', 'locks metrics', 'mongodb.locks_global', 'stacked'],
+        'lines': [
+            ['Global_R', 'shared', 'incremental'],
+            ['Global_W', 'exclusive', 'incremental'],
+            ['Global_r', 'intent_shared', 'incremental'],
+            ['Global_w', 'intent_exclusive', 'incremental']
+        ]},
+    'locks_metadata': {
+        'options': [None, 'Metadata lock. Number of times the lock was acquired in the specified mode',
+                    'locks', 'locks metrics', 'mongodb.locks_metadata', 'stacked'],
+        'lines': [
+            ['Metadata_R', 'shared', 'incremental'],
+            ['Metadata_w', 'intent_exclusive', 'incremental']
+        ]},
+    'locks_oplog': {
+        'options': [None, 'Lock on the oplog. Number of times the lock was acquired in the specified mode',
+                    'locks', 'locks metrics', 'mongodb.locks_oplog', 'stacked'],
+        'lines': [
+            ['Metadata_r', 'intent_shared', 'incremental'],
+            ['Metadata_w', 'intent_exclusive', 'incremental']
+        ]}
+}
+
+
+class Service(SimpleService):
+    def __init__(self, configuration=None, name=None):
+        SimpleService.__init__(self, configuration=configuration, name=name)
+        self.order = ORDER[:]
+        self.definitions = deepcopy(CHARTS)
+        self.user = self.configuration.get('user')
+        self.password = self.configuration.get('pass')
+        self.host = self.configuration.get('host', '127.0.0.1')
+        self.port = self.configuration.get('port', 27017)
+        self.timeout = self.configuration.get('timeout', 100)
+        self.metrics_to_collect = deepcopy(DEFAULT_METRICS)
+        self.connection = None
+        self.do_replica = None
+        self.databases = list()
+
+    def check(self):
+        if not PYMONGO:
+            self.error('Pymongo module is needed to use mongodb.chart.py')
+            return False
+        self.connection, server_status, error = self._create_connection()
+        if error:
+            self.error(error)
+            return False
+
+        self.build_metrics_to_collect_(server_status)
+
+        try:
+            self._get_data()
+        except (LookupError, SyntaxError, AttributeError):
+            self.error('Type: %s, error: %s' % (str(exc_info()[0]), str(exc_info()[1])))
+            return False
+        else:
+            self.create_charts_(server_status)
+            return True
+
+    def build_metrics_to_collect_(self, server_status):
+
+        self.do_replica = 'repl' in server_status
+        if 'dur' in server_status:
+            self.metrics_to_collect.extend(DUR)
+        if 'tcmalloc' in server_status:
+            self.metrics_to_collect.extend(TCMALLOC)
+        if 'commands' in server_status['metrics']:
+            self.metrics_to_collect.extend(COMMANDS)
+        if 'wiredTiger' in server_status:
+            self.metrics_to_collect.extend(WIREDTIGER)
+        if 'Collection' in server_status['locks']:
+            self.metrics_to_collect.extend(LOCKS)
+
+    def create_charts_(self, server_status):
+
+        if 'dur' not in server_status:
+            self.order.remove('journaling_transactions')
+            self.order.remove('journaling_volume')
+
+        if 'backgroundFlushing' not in server_status:
+            self.order.remove('background_flush_average')
+            self.order.remove('background_flush_last')
+            self.order.remove('background_flush_rate')
+
+        if 'wiredTiger' not in server_status:
+            self.order.remove('wiredtiger_write')
+            self.order.remove('wiredtiger_read')
+            self.order.remove('wiredtiger_cache')
+
+        if 'tcmalloc' not in server_status:
+            self.order.remove('tcmalloc_generic')
+            self.order.remove('tcmalloc_metrics')
+
+        if 'commands' not in server_status['metrics']:
+            self.order.remove('command_total_rate')
+            self.order.remove('command_failed_rate')
+
+        if 'Collection' not in server_status['locks']:
+            self.order.remove('locks_collection')
+            self.order.remove('locks_database')
+            self.order.remove('locks_global')
+            self.order.remove('locks_metadata')
+
+        if 'oplog' not in server_status['locks']:
+            self.order.remove('locks_oplog')
+
+        for dbase in self.databases:
+            self.order.append('_'.join([dbase, 'dbstats']))
+            self.definitions['_'.join([dbase, 'dbstats'])] = {
+                'options': [None, '%s: size of all documents, indexes, extents' % dbase, 'KB',
+                            'storage size metrics', 'mongodb.dbstats', 'line'],
+                'lines': [
+                    ['_'.join([dbase, 'dataSize']), 'documents', 'absolute', 1, 1024],
+                    ['_'.join([dbase, 'indexSize']), 'indexes', 'absolute', 1, 1024],
+                    ['_'.join([dbase, 'storageSize']), 'extents', 'absolute', 1, 1024]
+                ]}
+            self.definitions['dbstats_objects']['lines'].append(['_'.join([dbase, 'objects']), dbase, 'absolute'])
+
+        if self.do_replica:
+            def create_lines(hosts, string):
+                lines = list()
+                for host in hosts:
+                    dim_id = '_'.join([host, string])
+                    lines.append([dim_id, host, 'absolute', 1, 1000])
+                return lines
+
+            def create_state_lines(states):
+                lines = list()
+                for state, description in states:
+                    dim_id = '_'.join([host, 'state', state])
+                    lines.append([dim_id, description, 'absolute', 1, 1])
+                return lines
+
+            all_hosts = server_status['repl']['hosts']
+            this_host = server_status['repl']['me']
+            other_hosts = [host for host in all_hosts if host != this_host]
+
+            if 'local' in self.databases:
+                self.order.append('oplog_window')
+                self.definitions['oplog_window'] = {
+                    'options': [None, 'Interval of time between the oldest and the latest entries in the oplog',
+                                'seconds', 'replication and oplog', 'mongodb.oplog_window', 'line'],
+                    'lines': [['timeDiff', 'window', 'absolute', 1, 1000]]}
+            # Create "heartbeat delay" chart
+            self.order.append('heartbeat_delay')
+            self.definitions['heartbeat_delay'] = {
+                'options': [None, 'Time when last heartbeat was received'
+                                  ' from the replica set member (lastHeartbeatRecv)',
+                            'seconds ago', 'replication and oplog', 'mongodb.replication_heartbeat_delay', 'stacked'],
+                'lines': create_lines(other_hosts, 'heartbeat_lag')}
+            # Create "optimedate delay" chart
+            self.order.append('optimedate_delay')
+            self.definitions['optimedate_delay'] = {
+                'options': [None, 'Time when last entry from the oplog was applied (optimeDate)',
+                            'seconds ago', 'replication and oplog', 'mongodb.replication_optimedate_delay', 'stacked'],
+                'lines': create_lines(all_hosts, 'optimedate')}
+            # Create "replica set members state" chart
+            for host in all_hosts:
+                chart_name = '_'.join([host, 'state'])
+                self.order.append(chart_name)
+                self.definitions[chart_name] = {
+                    'options': [None, 'Replica set member (%s) current state' % host, 'state',
+                                'replication and oplog', 'mongodb.replication_state', 'line'],
+                    'lines': create_state_lines(REPLSET_STATES)}
+
+    def _get_raw_data(self):
+        raw_data = dict()
+
+        raw_data.update(self.get_serverstatus_() or dict())
+        raw_data.update(self.get_dbstats_() or dict())
+        raw_data.update(self.get_replsetgetstatus_() or dict())
+        raw_data.update(self.get_getreplicationinfo_() or dict())
+
+        return raw_data or None
+
+    def get_serverstatus_(self):
+        raw_data = dict()
+        try:
+            raw_data['serverStatus'] = self.connection.admin.command('serverStatus')
+        except PyMongoError:
+            return None
+        else:
+            return raw_data
+
+    def get_dbstats_(self):
+        if not self.databases:
+            return None
+
+        raw_data = dict()
+        raw_data['dbStats'] = dict()
+        try:
+            for dbase in self.databases:
+                raw_data['dbStats'][dbase] = self.connection[dbase].command('dbStats')
+        except PyMongoError:
+            return None
+        else:
+            return raw_data
+
+    def get_replsetgetstatus_(self):
+        if not self.do_replica:
+            return None
+
+        raw_data = dict()
+        try:
+            raw_data['replSetGetStatus'] = self.connection.admin.command('replSetGetStatus')
+        except PyMongoError:
+            return None
+        else:
+            return raw_data
+
+    def get_getreplicationinfo_(self):
+        if not (self.do_replica and 'local' in self.databases):
+            return None
+
+        raw_data = dict()
+        raw_data['getReplicationInfo'] = dict()
+        try:
+            raw_data['getReplicationInfo']['ASCENDING'] = self.connection.local.oplog.rs.find().sort(
+                "$natural", ASCENDING).limit(1)[0]
+            raw_data['getReplicationInfo']['DESCENDING'] = self.connection.local.oplog.rs.find().sort(
+                "$natural", DESCENDING).limit(1)[0]
+        except PyMongoError:
+            return None
+        else:
+            return raw_data
+
+    def _get_data(self):
+        """
+        :return: dict
+        """
+        raw_data = self._get_raw_data()
+
+        if not raw_data:
+            return None
+
+        to_netdata = dict()
+        serverStatus = raw_data['serverStatus']
+        dbStats = raw_data.get('dbStats')
+        replSetGetStatus = raw_data.get('replSetGetStatus')
+        getReplicationInfo = raw_data.get('getReplicationInfo')
+        utc_now = datetime.utcnow()
+
+        # serverStatus
+        for metric, new_name, function in self.metrics_to_collect:
+            value = serverStatus
+            for key in metric.split('.'):
+                try:
+                    value = value[key]
+                except KeyError:
+                    break
+
+            if not isinstance(value, dict) and key:
+                to_netdata[new_name or key] = value if not function else function(value)
+
+        to_netdata['nonmapped'] = to_netdata['virtual'] - serverStatus['mem'].get('mappedWithJournal',
+                                                                                  to_netdata['mapped'])
+        if to_netdata.get('maximum bytes configured'):
+            maximum = to_netdata['maximum bytes configured']
+            to_netdata['wiredTiger_percent_clean'] = int(to_netdata['bytes currently in the cache']
+                                                         * 100 / maximum * 1000)
+            to_netdata['wiredTiger_percent_dirty'] = int(to_netdata['tracked dirty bytes in the cache']
+                                                         * 100 / maximum * 1000)
+
+        # dbStats
+        if dbStats:
+            for dbase in dbStats:
+                for metric in DBSTATS:
+                    key = '_'.join([dbase, metric])
+                    to_netdata[key] = dbStats[dbase][metric]
+
+        # replSetGetStatus
+        if replSetGetStatus:
+            other_hosts = list()
+            members = replSetGetStatus['members']
+            unix_epoch = datetime(1970, 1, 1, 0, 0)
+
+            for member in members:
+                if not member.get('self'):
+                    other_hosts.append(member)
+                # Replica set time diff between current time and time when last entry from the oplog was applied
+                if member['optimeDate'] != unix_epoch:
+                    member_optimedate = member['name'] + '_optimedate'
+                    to_netdata.update({member_optimedate: int(delta_calculation(delta=utc_now - member['optimeDate'],
+                                                                                multiplier=1000))})
+                # Replica set members state
+                member_state = member['name'] + '_state'
+                for elem in REPLSET_STATES:
+                    state = elem[0]
+                    to_netdata.update({'_'.join([member_state, state]): 0})
+                to_netdata.update({'_'.join([member_state, str(member['state'])]): member['state']})
+            # Heartbeat lag calculation
+            for other in other_hosts:
+                if other['lastHeartbeatRecv'] != unix_epoch:
+                    node = other['name'] + '_heartbeat_lag'
+                    to_netdata[node] = int(delta_calculation(delta=utc_now - other['lastHeartbeatRecv'],
+                                                             multiplier=1000))
+
+        if getReplicationInfo:
+            first_event = getReplicationInfo['ASCENDING']['ts'].as_datetime()
+            last_event = getReplicationInfo['DESCENDING']['ts'].as_datetime()
+            to_netdata['timeDiff'] = int(delta_calculation(delta=last_event - first_event, multiplier=1000))
+
+        return to_netdata
+
+    def _create_connection(self):
+        conn_vars = {'host': self.host, 'port': self.port}
+        if hasattr(MongoClient, 'server_selection_timeout'):
+            conn_vars.update({'serverselectiontimeoutms': self.timeout})
+        try:
+            connection = MongoClient(**conn_vars)
+            if self.user and self.password:
+                connection.admin.authenticate(name=self.user, password=self.password)
+            # elif self.user:
+            #     connection.admin.authenticate(name=self.user, mechanism='MONGODB-X509')
+            server_status = connection.admin.command('serverStatus')
+        except PyMongoError as error:
+            return None, None, str(error)
+        else:
+            try:
+                self.databases = connection.database_names()
+            except PyMongoError as error:
+                self.info('Can\'t collect databases: %s' % str(error))
+            return connection, server_status, None
+
+
+def delta_calculation(delta, multiplier=1):
+    if hasattr(delta, 'total_seconds'):
+        return delta.total_seconds() * multiplier
+    else:
+        return (delta.microseconds + (delta.seconds + delta.days * 24 * 3600) * 10 ** 6) / 10.0 ** 6 * multiplier
index cb3254fdb24b2d5c86c6443001bb6ba48d1c86c4..8fb8217889c4f672e82803d55844d1d32004e454 100644 (file)
@@ -31,6 +31,7 @@ from subprocess import Popen, PIPE
 
 import threading
 import msg
+import ssl
 
 try:
     PATH = os.getenv('PATH').split(':')
@@ -460,7 +461,17 @@ class UrlService(SimpleService):
 
     def __add_openers(self):
         # TODO add error handling
-        self.opener = urllib2.build_opener()
+        if self.ss_cert:
+            try:
+                ctx = ssl.create_default_context()
+                ctx.check_hostname = False
+                ctx.verify_mode = ssl.CERT_NONE
+                self.opener = urllib2.build_opener(urllib2.HTTPSHandler(context=ctx))
+            except Exception as error:
+                self.error(str(error))
+                self.opener = urllib2.build_opener()
+        else:
+            self.opener = urllib2.build_opener()
 
         # Proxy handling
         # TODO currently self.proxies isn't parsed from configuration file
@@ -535,7 +546,7 @@ class UrlService(SimpleService):
             self.password = str(self.configuration['pass'])
         except (KeyError, TypeError):
             pass
-
+        self.ss_cert = self.configuration.get('ss_cert')
         self.__add_openers()
 
         test = self._get_data()
index c8b43595e10b0a0b7e1563fc13e6d9eac15b152e..afe45531d80793e8da2f8feb1447e39d93bb0e23 100644 (file)
@@ -14,7 +14,7 @@ priority = 60000
 retries = 60
 
 ORDER = ['response_statuses', 'response_codes', 'bandwidth', 'response_time', 'requests_per_url', 'http_method',
-         'requests_per_ipproto', 'clients', 'clients_all']
+         'http_version', 'requests_per_ipproto', 'clients', 'clients_all']
 CHARTS = {
     'response_codes': {
         'options': [None, 'Response Codes', 'requests/s', 'responses', 'web_log.response_codes', 'stacked'],
@@ -57,6 +57,10 @@ CHARTS = {
         'lines': [
             ['GET', 'GET', 'incremental', 1, 1]
         ]},
+    'http_version': {
+        'options': [None, 'Requests Per HTTP Version', 'requests/s', 'http versions',
+                    'web_log.http_version', 'stacked'],
+        'lines': []},
     'requests_per_ipproto': {
         'options': [None, 'Requests Per IP Protocol', 'requests/s', 'ip protocols', 'web_log.requests_per_ipproto',
                     'stacked'],
@@ -234,7 +238,8 @@ class Service(LogService):
                               'method': r'[A-Z]+',
                               'bytes_sent': r'\d+|-'}
             optional_dict = {'resp_length': r'\d+',
-                             'resp_time': r'[\d.]+'}
+                             'resp_time': r'[\d.]+',
+                             'http_version': r'\d\.\d'}
 
             mandatory_values = set(mandatory_dict) - set(match_dict)
             if mandatory_values:
@@ -275,13 +280,15 @@ class Service(LogService):
         # 5. Bytes sent 6. Response length 7. Response process time
         acs_default = re.compile(r'(?P<address>[\da-f.:]+)'
                                  r' -.*?"(?P<method>[A-Z]+)'
-                                 r' (?P<url>.*?)"'
+                                 r' (?P<url>[^ ]+)'
+                                 r' [A-Z]+/(?P<http_version>\d\.\d)"'
                                  r' (?P<code>[1-9]\d{2})'
                                  r' (?P<bytes_sent>\d+|-)')
 
         acs_apache_ext_insert = re.compile(r'(?P<address>[\da-f.:]+)'
                                            r' -.*?"(?P<method>[A-Z]+)'
-                                           r' (?P<url>.*?)"'
+                                           r' (?P<url>[^ ]+)'
+                                           r' [A-Z]+/(?P<http_version>\d\.\d)"'
                                            r' (?P<code>[1-9]\d{2})'
                                            r' (?P<bytes_sent>\d+|-)'
                                            r' (?P<resp_length>\d+)'
@@ -289,7 +296,8 @@ class Service(LogService):
 
         acs_apache_ext_append = re.compile(r'(?P<address>[\da-f.:]+)'
                                            r' -.*?"(?P<method>[A-Z]+)'
-                                           r' (?P<url>.*?)"'
+                                           r' (?P<url>[^ ]+)'
+                                           r' [A-Z]+/(?P<http_version>\d\.\d)"'
                                            r' (?P<code>[1-9]\d{2})'
                                            r' (?P<bytes_sent>\d+|-)'
                                            r' .*?'
@@ -299,7 +307,8 @@ class Service(LogService):
 
         acs_nginx_ext_insert = re.compile(r'(?P<address>[\da-f.:]+)'
                                           r' -.*?"(?P<method>[A-Z]+)'
-                                          r' (?P<url>.*?)"'
+                                          r' (?P<url>[^ ]+)'
+                                          r' [A-Z]+/(?P<http_version>\d\.\d)"'
                                           r' (?P<code>[1-9]\d{2})'
                                           r' (?P<bytes_sent>\d+)'
                                           r' (?P<resp_length>\d+)'
@@ -307,7 +316,8 @@ class Service(LogService):
 
         acs_nginx_ext_append = re.compile(r'(?P<address>[\da-f.:]+)'
                                           r' -.*?"(?P<method>[A-Z]+)'
-                                          r' (?P<url>.*?)"'
+                                          r' (?P<url>[^ ]+)'
+                                          r' [A-Z]+/(?P<http_version>\d\.\d)"'
                                           r' (?P<code>[1-9]\d{2})'
                                           r' (?P<bytes_sent>\d+)'
                                           r' .*?'
@@ -369,6 +379,9 @@ class Service(LogService):
                                  ' "" "Requests Per HTTP Method" requests/s "http methods"' \
                                  ' web_log.http_method stacked 2 %s\n' \
                                  'DIMENSION GET GET incremental\n' % (job_name, self.update_every)
+        self.http_version_chart = 'CHART %s.http_version' \
+                                  ' "" "Requests Per HTTP Version" requests/s "http versions"' \
+                                  ' web_log.http_version stacked 3 %s\n' % (job_name, self.update_every)
 
         # Remove 'request_time' chart from ORDER if resp_time not in match_dict
         if 'resp_time' not in match_dict:
@@ -448,6 +461,9 @@ class Service(LogService):
                     self._get_data_per_url(match_dict['url'])
                 # requests per http method
                 self._get_data_http_method(match_dict['method'])
+                # requests per http version
+                if 'http_version' in match_dict:
+                    self._get_data_http_version(match_dict['http_version'])
                 # bandwidth sent
                 bytes_sent = match_dict['bytes_sent'] if '-' not in match_dict['bytes_sent'] else 0
                 self.data['bytes_sent'] += int(bytes_sent)
@@ -503,6 +519,20 @@ class Service(LogService):
                                                             chart_string_copy, 'http_method')
         self.data[method] += 1
 
+    def _get_data_http_version(self, http_version):
+        """
+        :param http_version: str: METHOD from parsed line. Ex.: '1.1', '1.0'
+        :return:
+        Calls add_new_dimension method If the value is found for the first time
+        """
+        http_version_dim_id = http_version.replace('.', '_')
+        if http_version_dim_id not in self.data:
+            chart_string_copy = self.http_version_chart
+            self.http_version_chart = self.add_new_dimension(http_version_dim_id,
+                                                             [http_version_dim_id, http_version, 'incremental'],
+                                                             chart_string_copy, 'http_version')
+        self.data[http_version_dim_id] += 1
+
     def _get_data_per_url(self, url):
         """
         :param url: str: URL from parsed line
index bec3ef92d80db15b9264ce6481211bbe781de399..a87cd7a8f4f949d2be8aa343791301d0ce7c5c6e 100644 (file)
@@ -39,7 +39,7 @@ netdata_SOURCES = \
        dictionary.c dictionary.h \
        eval.c eval.h \
        global_statistics.c global_statistics.h \
-       health.c health.h \
+       health.c health.h health_log.c health_config.c health_json.c \
        inlined.h \
        log.c log.h \
        main.c main.h \
@@ -65,10 +65,22 @@ netdata_SOURCES = \
        registry_db.c \
        registry_log.c \
        rrd.c rrd.h \
+       rrddim.c \
+       rrdfamily.c \
+       rrdhost.c \
+       rrdset.c \
+       rrdcalc.c \
+       rrdcalctemplate.c \
+       rrdvar.c \
+       rrddimvar.c \
+       rrdsetvar.c \
        rrd2json.c rrd2json.h \
+       rrdpush.c rrdpush.h \
        storage_number.c storage_number.h \
        unit_test.c unit_test.h \
        url.c url.h \
+       web_api_old.c web_api_old.h \
+       web_api_v1.c web_api_v1.h \
        web_buffer.c web_buffer.h \
        web_buffer_svg.c web_buffer_svg.h \
        web_client.c web_client.h \
@@ -146,19 +158,3 @@ apps_plugin_LDADD = \
        $(OPTIONAL_CAP_LIBS) \
        $(NULL)
 
-install-data-hook:
-       if [ `id -u` == 0 ]; then \
-               chown root '$(DESTDIR)$(pluginsdir)/apps.plugin' && \
-               chmod 0755 '$(DESTDIR)$(pluginsdir)/apps.plugin' && \
-               ( setcap cap_dac_read_search,cap_sys_ptrace+ep '$(DESTDIR)$(pluginsdir)/apps.plugin' || \
-                 chmod 4755 '$(DESTDIR)$(pluginsdir)/apps.plugin' ); \
-       else \
-               echo; \
-               echo "ATTENTION"; \
-               echo; \
-               echo "$(pluginsdir)/apps.plugin requires escalated capabilities:"; \
-               echo "sudo chown root '$(DESTDIR)$(pluginsdir)/apps.plugin'"; \
-               echo "sudo chmod 0755 '$(DESTDIR)$(pluginsdir)/apps.plugin'"; \
-               echo "sudo setcap cap_dac_read_search,cap_sys_ptrace+ep '$(DESTDIR)$(pluginsdir)/apps.plugin'"; \
-               echo; \
-       fi
index ac525dc6c14b3fadb418a7a3e9ab442c50821625..7ae8ba1459f2c0eef7d64c0e7cd2aa14674c1cf0 100644 (file)
@@ -65,6 +65,7 @@ void arl_begin(ARL_BASE *base) {
             info("ARL '%s' has %zu fast searches and %zu slow searches. Is the source really changing so fast?"
                  , base->name, base->fast, base->slow);
 
+        /*
         if(unlikely(base->iteration % 60 == 0)) {
             info("ARL '%s' statistics: iteration %zu, expected %zu, wanted %zu, allocated %zu, fred %zu, relinkings %zu, found %zu, added %zu, fast %zu, slow %zu"
                  , base->name
@@ -82,6 +83,7 @@ void arl_begin(ARL_BASE *base) {
             // for(e = base->head; e; e = e->next) fprintf(stderr, "%s ", e->name);
             // fprintf(stderr, "\n");
         }
+        */
     }
 #endif
 
index 81ab01be2cda7016ad6ffcbe7a9465dec723b42a..50e4c31fbdcb017d81874c5af7efabc258365f69 100644 (file)
@@ -2,8 +2,6 @@
 
 #define CONFIG_FILE_LINE_MAX ((CONFIG_MAX_NAME + CONFIG_MAX_VALUE + 1024) * 2)
 
-pthread_mutex_t config_mutex = PTHREAD_MUTEX_INITIALIZER;
-
 // ----------------------------------------------------------------------------
 // definitions
 
@@ -12,7 +10,7 @@ pthread_mutex_t config_mutex = PTHREAD_MUTEX_INITIALIZER;
 #define CONFIG_VALUE_CHANGED 0x04 // has been changed from the loaded value
 #define CONFIG_VALUE_CHECKED 0x08 // has been checked if the value is different from the default
 
-struct config_value {
+struct config_option {
     avl avl;                // the index - this has to be first!
 
     uint8_t flags;
@@ -22,10 +20,10 @@ struct config_value {
     char *name;
     char *value;
 
-    struct config_value *next; // config->mutex protects just this
+    struct config_option *next; // config->mutex protects just this
 };
 
-struct config {
+struct section {
     avl avl;
 
     uint32_t hash;          // a simple hash to speed up searching
@@ -33,32 +31,51 @@ struct config {
 
     char *name;
 
-    struct config *next;    // gloabl config_mutex protects just this
+    struct section *next;    // gloabl config_mutex protects just this
 
-    struct config_value *values;
+    struct config_option *values;
     avl_tree_lock values_index;
 
     pthread_mutex_t mutex;  // this locks only the writers, to ensure atomic updates
                             // readers are protected using the rwlock in avl_tree_lock
-} *config_root = NULL;
+};
+
+static int appconfig_section_compare(void *a, void *b);
+
+struct config netdata_config = {
+        .sections = NULL,
+        .mutex = PTHREAD_MUTEX_INITIALIZER,
+        .index = {
+            { NULL, appconfig_section_compare },
+            AVL_LOCK_INITIALIZER
+        }
+};
 
+struct config stream_config = {
+        .sections = NULL,
+        .mutex = PTHREAD_MUTEX_INITIALIZER,
+        .index = {
+                { NULL, appconfig_section_compare },
+                AVL_LOCK_INITIALIZER
+        }
+};
 
 // ----------------------------------------------------------------------------
 // locking
 
-static inline void config_global_write_lock(void) {
-    pthread_mutex_lock(&config_mutex);
+static inline void appconfig_wrlock(struct config *root) {
+    pthread_mutex_lock(&root->mutex);
 }
 
-static inline void config_global_unlock(void) {
-    pthread_mutex_unlock(&config_mutex);
+static inline void appconfig_unlock(struct config *root) {
+    pthread_mutex_unlock(&root->mutex);
 }
 
-static inline void config_section_write_lock(struct config *co) {
+static inline void config_section_wrlock(struct section *co) {
     pthread_mutex_lock(&co->mutex);
 }
 
-static inline void config_section_unlock(struct config *co) {
+static inline void config_section_unlock(struct section *co) {
     pthread_mutex_unlock(&co->mutex);
 }
 
@@ -66,78 +83,72 @@ static inline void config_section_unlock(struct config *co) {
 // ----------------------------------------------------------------------------
 // config name-value index
 
-static int config_value_compare(void* a, void* b) {
-    if(((struct config_value *)a)->hash < ((struct config_value *)b)->hash) return -1;
-    else if(((struct config_value *)a)->hash > ((struct config_value *)b)->hash) return 1;
-    else return strcmp(((struct config_value *)a)->name, ((struct config_value *)b)->name);
+static int appconfig_option_compare(void *a, void *b) {
+    if(((struct config_option *)a)->hash < ((struct config_option *)b)->hash) return -1;
+    else if(((struct config_option *)a)->hash > ((struct config_option *)b)->hash) return 1;
+    else return strcmp(((struct config_option *)a)->name, ((struct config_option *)b)->name);
 }
 
-#define config_value_index_add(co, cv) (struct config_value *)avl_insert_lock(&((co)->values_index), (avl *)(cv))
-#define config_value_index_del(co, cv) (struct config_value *)avl_remove_lock(&((co)->values_index), (avl *)(cv))
+#define appconfig_option_index_add(co, cv) (struct config_option *)avl_insert_lock(&((co)->values_index), (avl *)(cv))
+#define appconfig_option_index_del(co, cv) (struct config_option *)avl_remove_lock(&((co)->values_index), (avl *)(cv))
 
-static struct config_value *config_value_index_find(struct config *co, const char *name, uint32_t hash) {
-    struct config_value tmp;
+static struct config_option *appconfig_option_index_find(struct section *co, const char *name, uint32_t hash) {
+    struct config_option tmp;
     tmp.hash = (hash)?hash:simple_hash(name);
     tmp.name = (char *)name;
 
-    return (struct config_value *)avl_search_lock(&(co->values_index), (avl *) &tmp);
+    return (struct config_option *)avl_search_lock(&(co->values_index), (avl *) &tmp);
 }
 
 
 // ----------------------------------------------------------------------------
 // config sections index
 
-static int config_compare(void* a, void* b) {
-    if(((struct config *)a)->hash < ((struct config *)b)->hash) return -1;
-    else if(((struct config *)a)->hash > ((struct config *)b)->hash) return 1;
-    else return strcmp(((struct config *)a)->name, ((struct config *)b)->name);
+static int appconfig_section_compare(void *a, void *b) {
+    if(((struct section *)a)->hash < ((struct section *)b)->hash) return -1;
+    else if(((struct section *)a)->hash > ((struct section *)b)->hash) return 1;
+    else return strcmp(((struct section *)a)->name, ((struct section *)b)->name);
 }
 
-avl_tree_lock config_root_index = {
-        { NULL, config_compare },
-        AVL_LOCK_INITIALIZER
-};
-
-#define config_index_add(cfg) (struct config *)avl_insert_lock(&config_root_index, (avl *)(cfg))
-#define config_index_del(cfg) (struct config *)avl_remove_lock(&config_root_index, (avl *)(cfg))
+#define appconfig_index_add(root, cfg) (struct section *)avl_insert_lock(&root->index, (avl *)(cfg))
+#define appconfig_index_del(root, cfg) (struct section *)avl_remove_lock(&root->index, (avl *)(cfg))
 
-static struct config *config_index_find(const char *name, uint32_t hash) {
-    struct config tmp;
+static struct section *appconfig_index_find(struct config *root, const char *name, uint32_t hash) {
+    struct section tmp;
     tmp.hash = (hash)?hash:simple_hash(name);
     tmp.name = (char *)name;
 
-    return (struct config *)avl_search_lock(&config_root_index, (avl *) &tmp);
+    return (struct section *)avl_search_lock(&root->index, (avl *) &tmp);
 }
 
 
 // ----------------------------------------------------------------------------
 // config section methods
 
-static inline struct config *config_section_find(const char *section) {
-    return config_index_find(section, 0);
+static inline struct section *appconfig_section_find(struct config *root, const char *section) {
+    return appconfig_index_find(root, section, 0);
 }
 
-static inline struct config *config_section_create(const char *section)
-{
+static inline struct section *appconfig_section_create(struct config *root, const char *section) {
     debug(D_CONFIG, "Creating section '%s'.", section);
 
-    struct config *co = callocz(1, sizeof(struct config));
+    struct section *co = callocz(1, sizeof(struct section));
     co->name = strdupz(section);
     co->hash = simple_hash(co->name);
 
-    avl_init_lock(&co->values_index, config_value_compare);
+    avl_init_lock(&co->values_index, appconfig_option_compare);
 
-    if(unlikely(config_index_add(co) != co))
+    if(unlikely(appconfig_index_add(root, co) != co))
         error("INTERNAL ERROR: indexing of section '%s', already exists.", co->name);
 
-    config_global_write_lock();
-    struct config *co2 = config_root;
+    appconfig_wrlock(root);
+    struct section *co2 = root->sections;
     if(co2) {
         while (co2->next) co2 = co2->next;
         co2->next = co;
     }
-    else config_root = co;
-    config_global_unlock();
+    else root->sections = co;
+    appconfig_unlock(root);
 
     return co;
 }
@@ -146,20 +157,25 @@ static inline struct config *config_section_create(const char *section)
 // ----------------------------------------------------------------------------
 // config name-value methods
 
-static inline struct config_value *config_value_create(struct config *co, const char *name, const char *value)
-{
+static inline struct config_option *appconfig_value_create(struct section *co, const char *name, const char *value) {
     debug(D_CONFIG, "Creating config entry for name '%s', value '%s', in section '%s'.", name, value, co->name);
 
-    struct config_value *cv = callocz(1, sizeof(struct config_value));
+    struct config_option *cv = callocz(1, sizeof(struct config_option));
     cv->name = strdupz(name);
     cv->hash = simple_hash(cv->name);
     cv->value = strdupz(value);
 
-    if(unlikely(config_value_index_add(co, cv) != cv))
-        error("INTERNAL ERROR: indexing of config '%s' in section '%s': already exists.", cv->name, co->name);
+    struct config_option *found = appconfig_option_index_add(co, cv);
+    if(found != cv) {
+        error("indexing of config '%s' in section '%s': already exists - using the existing one.", cv->name, co->name);
+        freez(cv->value);
+        freez(cv->name);
+        freez(cv);
+        return found;
+    }
 
-    config_section_write_lock(co);
-    struct config_value *cv2 = co->values;
+    config_section_wrlock(co);
+    struct config_option *cv2 = co->values;
     if(cv2) {
         while (cv2->next) cv2 = cv2->next;
         cv2->next = cv;
@@ -170,66 +186,87 @@ static inline struct config_value *config_value_create(struct config *co, const
     return cv;
 }
 
-int config_exists(const char *section, const char *name) {
-    struct config_value *cv;
+int appconfig_exists(struct config *root, const char *section, const char *name) {
+    struct config_option *cv;
 
     debug(D_CONFIG, "request to get config in section '%s', name '%s'", section, name);
 
-    struct config *co = config_section_find(section);
+    struct section *co = appconfig_section_find(root, section);
     if(!co) return 0;
 
-    cv = config_value_index_find(co, name, 0);
+    cv = appconfig_option_index_find(co, name, 0);
     if(!cv) return 0;
 
     return 1;
 }
 
-int config_rename(const char *section, const char *old, const char *new) {
-    struct config_value *cv, *cv2;
+int appconfig_move(struct config *root, const char *section_old, const char *name_old, const char *section_new, const char *name_new) {
+    struct config_option *cv_old, *cv_new;
+    int ret = -1;
 
-    debug(D_CONFIG, "request to rename config in section '%s', old name '%s', new name '%s'", section, old, new);
+    debug(D_CONFIG, "request to rename config in section '%s', old name '%s', to section '%s', new name '%s'", section_old, name_old, section_new, name_new);
 
-    struct config *co = config_section_find(section);
-    if(!co) return -1;
+    struct section *co_old = appconfig_section_find(root, section_old);
+    if(!co_old) return ret;
 
-    config_section_write_lock(co);
+    struct section *co_new = appconfig_section_find(root, section_new);
+    if(!co_new) co_new = appconfig_section_create(root, section_new);
 
-    cv = config_value_index_find(co, old, 0);
-    if(!cv) goto cleanup;
+    config_section_wrlock(co_old);
+    config_section_wrlock(co_new);
 
-    cv2 = config_value_index_find(co, new, 0);
-    if(cv2) goto cleanup;
+    cv_old = appconfig_option_index_find(co_old, name_old, 0);
+    if(!cv_old) goto cleanup;
 
-    if(unlikely(config_value_index_del(co, cv) != cv))
-        error("INTERNAL ERROR: deletion of config '%s' from section '%s', deleted tge wrong config entry.", cv->name, co->name);
+    cv_new = appconfig_option_index_find(co_new, name_new, 0);
+    if(cv_new) goto cleanup;
 
-    freez(cv->name);
-    cv->name = strdupz(new);
-    cv->hash = simple_hash(cv->name);
-    if(unlikely(config_value_index_add(co, cv) != cv))
-        error("INTERNAL ERROR: indexing of config '%s' in section '%s', already exists.", cv->name, co->name);
+    if(unlikely(appconfig_option_index_del(co_old, cv_old) != cv_old))
+        error("INTERNAL ERROR: deletion of config '%s' from section '%s', deleted tge wrong config entry.", cv_old->name, co_old->name);
 
-    config_section_unlock(co);
+    if(co_old->values == cv_old) {
+        co_old->values = cv_old->next;
+    }
+    else {
+        struct config_option *t;
+        for(t = co_old->values; t && t->next != cv_old ;t = t->next) ;
+        if(!t || t->next != cv_old)
+            error("INTERNAL ERROR: cannot find variable '%s' in section '%s' of the config - but it should be there.", cv_old->name, co_old->name);
+        else
+            t->next = cv_old->next;
+    }
 
-    return 0;
+    freez(cv_old->name);
+    cv_old->name = strdupz(name_new);
+    cv_old->hash = simple_hash(cv_old->name);
+
+    cv_new = cv_old;
+    cv_new->next = co_new->values;
+    co_new->values = cv_new;
+
+    if(unlikely(appconfig_option_index_add(co_new, cv_old) != cv_old))
+        error("INTERNAL ERROR: re-indexing of config '%s' in section '%s', already exists.", cv_old->name, co_new->name);
+
+    ret = 0;
 
 cleanup:
-    config_section_unlock(co);
-    return -1;
+    config_section_unlock(co_new);
+    config_section_unlock(co_old);
+    return ret;
 }
 
-char *config_get(const char *section, const char *name, const char *default_value)
+char *appconfig_get(struct config *root, const char *section, const char *name, const char *default_value)
 {
-    struct config_value *cv;
+    struct config_option *cv;
 
     debug(D_CONFIG, "request to get config in section '%s', name '%s', default_value '%s'", section, name, default_value);
 
-    struct config *co = config_section_find(section);
-    if(!co) co = config_section_create(section);
+    struct section *co = appconfig_section_find(root, section);
+    if(!co) co = appconfig_section_create(root, section);
 
-    cv = config_value_index_find(co, name, 0);
+    cv = appconfig_option_index_find(co, name, 0);
     if(!cv) {
-        cv = config_value_create(co, name, default_value);
+        cv = appconfig_value_create(co, name, default_value);
         if(!cv) return NULL;
     }
     cv->flags |= CONFIG_VALUE_USED;
@@ -246,67 +283,67 @@ char *config_get(const char *section, const char *name, const char *default_valu
     return(cv->value);
 }
 
-long long config_get_number(const char *section, const char *name, long long value)
+long long appconfig_get_number(struct config *root, const char *section, const char *name, long long value)
 {
     char buffer[100], *s;
     sprintf(buffer, "%lld", value);
 
-    s = config_get(section, name, buffer);
+    s = appconfig_get(root, section, name, buffer);
     if(!s) return value;
 
     return strtoll(s, NULL, 0);
 }
 
-int config_get_boolean(const char *section, const char *name, int value)
+int appconfig_get_boolean(struct config *root, const char *section, const char *name, int value)
 {
     char *s;
     if(value) s = "yes";
     else s = "no";
 
-    s = config_get(section, name, s);
+    s = appconfig_get(root, section, name, s);
     if(!s) return value;
 
     if(!strcmp(s, "yes") || !strcmp(s, "auto") || !strcmp(s, "on demand")) return 1;
     return 0;
 }
 
-int config_get_boolean_ondemand(const char *section, const char *name, int value)
+int appconfig_get_boolean_ondemand(struct config *root, const char *section, const char *name, int value)
 {
     char *s;
 
-    if(value == CONFIG_ONDEMAND_ONDEMAND)
+    if(value == CONFIG_BOOLEAN_AUTO)
         s = "auto";
 
-    else if(value == CONFIG_ONDEMAND_NO)
+    else if(value == CONFIG_BOOLEAN_NO)
         s = "no";
 
     else
         s = "yes";
 
-    s = config_get(section, name, s);
+    s = appconfig_get(root, section, name, s);
     if(!s) return value;
 
     if(!strcmp(s, "yes"))
-        return CONFIG_ONDEMAND_YES;
+        return CONFIG_BOOLEAN_YES;
     else if(!strcmp(s, "no"))
-        return CONFIG_ONDEMAND_NO;
+        return CONFIG_BOOLEAN_NO;
     else if(!strcmp(s, "auto") || !strcmp(s, "on demand"))
-        return CONFIG_ONDEMAND_ONDEMAND;
+        return CONFIG_BOOLEAN_AUTO;
 
     return value;
 }
 
-const char *config_set_default(const char *section, const char *name, const char *value)
+const char *appconfig_set_default(struct config *root, const char *section, const char *name, const char *value)
 {
-    struct config_value *cv;
+    struct config_option *cv;
 
     debug(D_CONFIG, "request to set config in section '%s', name '%s', value '%s'", section, name, value);
 
-    struct config *co = config_section_find(section);
-    if(!co) return config_set(section, name, value);
+    struct section *co = appconfig_section_find(root, section);
+    if(!co) return appconfig_set(root, section, name, value);
 
-    cv = config_value_index_find(co, name, 0);
-    if(!cv) return config_set(section, name, value);
+    cv = appconfig_option_index_find(co, name, 0);
+    if(!cv) return appconfig_set(root, section, name, value);
 
     cv->flags |= CONFIG_VALUE_USED;
 
@@ -323,17 +360,17 @@ const char *config_set_default(const char *section, const char *name, const char
     return cv->value;
 }
 
-const char *config_set(const char *section, const char *name, const char *value)
+const char *appconfig_set(struct config *root, const char *section, const char *name, const char *value)
 {
-    struct config_value *cv;
+    struct config_option *cv;
 
     debug(D_CONFIG, "request to set config in section '%s', name '%s', value '%s'", section, name, value);
 
-    struct config *co = config_section_find(section);
-    if(!co) co = config_section_create(section);
+    struct section *co = appconfig_section_find(root, section);
+    if(!co) co = appconfig_section_create(root, section);
 
-    cv = config_value_index_find(co, name, 0);
-    if(!cv) cv = config_value_create(co, name, value);
+    cv = appconfig_option_index_find(co, name, 0);
+    if(!cv) cv = appconfig_value_create(co, name, value);
     cv->flags |= CONFIG_VALUE_USED;
 
     if(strcmp(cv->value, value) != 0) {
@@ -346,23 +383,23 @@ const char *config_set(const char *section, const char *name, const char *value)
     return value;
 }
 
-long long config_set_number(const char *section, const char *name, long long value)
+long long appconfig_set_number(struct config *root, const char *section, const char *name, long long value)
 {
     char buffer[100];
     sprintf(buffer, "%lld", value);
 
-    config_set(section, name, buffer);
+    appconfig_set(root, section, name, buffer);
 
     return value;
 }
 
-int config_set_boolean(const char *section, const char *name, int value)
+int appconfig_set_boolean(struct config *root, const char *section, const char *name, int value)
 {
     char *s;
     if(value) s = "yes";
     else s = "no";
 
-    config_set(section, name, s);
+    appconfig_set(root, section, name, s);
 
     return value;
 }
@@ -371,10 +408,10 @@ int config_set_boolean(const char *section, const char *name, int value)
 // ----------------------------------------------------------------------------
 // config load/save
 
-int load_config(char *filename, int overwrite_used)
+int appconfig_load(struct config *root, char *filename, int overwrite_used)
 {
     int line = 0;
-    struct config *co = NULL;
+    struct section *co = NULL;
 
     char buffer[CONFIG_FILE_LINE_MAX + 1], *s;
 
@@ -404,8 +441,8 @@ int load_config(char *filename, int overwrite_used)
             s[len - 1] = '\0';
             s++;
 
-            co = config_section_find(s);
-            if(!co) co = config_section_create(s);
+            co = appconfig_section_find(root, s);
+            if(!co) co = appconfig_section_create(root, s);
 
             continue;
         }
@@ -437,9 +474,9 @@ int load_config(char *filename, int overwrite_used)
             continue;
         }
 
-        struct config_value *cv = config_value_index_find(co, name, 0);
+        struct config_option *cv = appconfig_option_index_find(co, name, 0);
 
-        if(!cv) cv = config_value_create(co, name, value);
+        if(!cv) cv = appconfig_value_create(co, name, value);
         else {
             if(((cv->flags & CONFIG_VALUE_USED) && overwrite_used) || !(cv->flags & CONFIG_VALUE_USED)) {
                 debug(D_CONFIG, "Line %d, overwriting '%s/%s'.", line, co->name, cv->name);
@@ -457,11 +494,11 @@ int load_config(char *filename, int overwrite_used)
     return 1;
 }
 
-void generate_config(BUFFER *wb, int only_changed)
+void appconfig_generate(struct config *root, BUFFER *wb, int only_changed)
 {
     int i, pri;
-    struct config *co;
-    struct config_value *cv;
+    struct section *co;
+    struct config_option *cv;
 
     for(i = 0; i < 3 ;i++) {
         switch(i) {
@@ -490,13 +527,16 @@ void generate_config(BUFFER *wb, int only_changed)
                 break;
         }
 
-        config_global_write_lock();
-        for(co = config_root; co ; co = co->next) {
-            if(!strcmp(co->name, "global") ||
-                    !strcmp(co->name, "plugins")  ||
-                    !strcmp(co->name, "registry") ||
-                    !strcmp(co->name, "health")   ||
-                    !strcmp(co->name, "backend"))
+        appconfig_wrlock(root);
+        for(co = root->sections; co ; co = co->next) {
+            if(!strcmp(co->name, CONFIG_SECTION_GLOBAL)
+               || !strcmp(co->name, CONFIG_SECTION_WEB)
+               || !strcmp(co->name, CONFIG_SECTION_PLUGINS)
+               || !strcmp(co->name, CONFIG_SECTION_REGISTRY)
+               || !strcmp(co->name, CONFIG_SECTION_HEALTH)
+               || !strcmp(co->name, CONFIG_SECTION_BACKEND)
+               || !strcmp(co->name, CONFIG_SECTION_STREAM)
+                    )
                 pri = 0;
             else if(!strncmp(co->name, "plugin:", 7)) pri = 1;
             else pri = 2;
@@ -506,7 +546,7 @@ void generate_config(BUFFER *wb, int only_changed)
                 int changed = 0;
                 int count = 0;
 
-                config_section_write_lock(co);
+                config_section_wrlock(co);
                 for(cv = co->values; cv ; cv = cv->next) {
                     used += (cv->flags & CONFIG_VALUE_USED)?1:0;
                     changed += (cv->flags & CONFIG_VALUE_CHANGED)?1:0;
@@ -523,7 +563,7 @@ void generate_config(BUFFER *wb, int only_changed)
 
                 buffer_sprintf(wb, "\n[%s]\n", co->name);
 
-                config_section_write_lock(co);
+                config_section_wrlock(co);
                 for(cv = co->values; cv ; cv = cv->next) {
 
                     if(used && !(cv->flags & CONFIG_VALUE_USED)) {
@@ -534,6 +574,6 @@ void generate_config(BUFFER *wb, int only_changed)
                 config_section_unlock(co);
             }
         }
-        config_global_unlock();
+        appconfig_unlock(root);
     }
 }
index 08aae8348891c08fda9298a9a68046b50a59b973..15bba80786e8f5c353183f093d6f91ff378ad7a6 100644 (file)
@@ -3,30 +3,67 @@
 
 #define CONFIG_FILENAME "netdata.conf"
 
+#define CONFIG_SECTION_GLOBAL   "global"
+#define CONFIG_SECTION_WEB      "web"
+#define CONFIG_SECTION_PLUGINS  "plugins"
+#define CONFIG_SECTION_REGISTRY "registry"
+#define CONFIG_SECTION_HEALTH   "health"
+#define CONFIG_SECTION_BACKEND  "backend"
+#define CONFIG_SECTION_STREAM   "stream"
+
 // these are used to limit the configuration names and values lengths
 // they are not enforced by config.c functions (they will strdup() all strings, no matter of their length)
 #define CONFIG_MAX_NAME 1024
 #define CONFIG_MAX_VALUE 2048
 
-extern int load_config(char *filename, int overwrite_used);
+struct config {
+    struct section *sections;
+    pthread_mutex_t mutex;
+    avl_tree_lock index;
+};
+
+extern struct config
+        netdata_config,
+        stream_config;
+
+#define CONFIG_BOOLEAN_NO   0
+#define CONFIG_BOOLEAN_YES  1
+#define CONFIG_BOOLEAN_AUTO 2
+
+extern int appconfig_load(struct config *root, char *filename, int overwrite_used);
+
+extern char *appconfig_get(struct config *root, const char *section, const char *name, const char *default_value);
+extern long long appconfig_get_number(struct config *root, const char *section, const char *name, long long value);
+extern int appconfig_get_boolean(struct config *root, const char *section, const char *name, int value);
+extern int appconfig_get_boolean_ondemand(struct config *root, const char *section, const char *name, int value);
+
+extern const char *appconfig_set(struct config *root, const char *section, const char *name, const char *value);
+extern const char *appconfig_set_default(struct config *root, const char *section, const char *name, const char *value);
+extern long long appconfig_set_number(struct config *root, const char *section, const char *name, long long value);
+extern int appconfig_set_boolean(struct config *root, const char *section, const char *name, int value);
+
+extern int appconfig_exists(struct config *root, const char *section, const char *name);
+extern int appconfig_move(struct config *root, const char *section_old, const char *name_old, const char *section_new, const char *name_new);
+
+extern void appconfig_generate(struct config *root, BUFFER *wb, int only_changed);
 
-extern char *config_get(const char *section, const char *name, const char *default_value);
-extern long long config_get_number(const char *section, const char *name, long long value);
-extern int config_get_boolean(const char *section, const char *name, int value);
+// ----------------------------------------------------------------------------
+// shortcuts for the default netdata configuration
 
-#define CONFIG_ONDEMAND_NO 0
-#define CONFIG_ONDEMAND_YES 1
-#define CONFIG_ONDEMAND_ONDEMAND 2
-extern int config_get_boolean_ondemand(const char *section, const char *name, int value);
+#define config_load(filename, overwrite_used) appconfig_load(&netdata_config, filename, overwrite_used)
+#define config_get(section, name, default_value) appconfig_get(&netdata_config, section, name, default_value)
+#define config_get_number(section, name, value) appconfig_get_number(&netdata_config, section, name, value)
+#define config_get_boolean(section, name, value) appconfig_get_boolean(&netdata_config, section, name, value)
+#define config_get_boolean_ondemand(section, name, value) appconfig_get_boolean_ondemand(&netdata_config, section, name, value)
 
-extern const char *config_set(const char *section, const char *name, const char *value);
-extern const char *config_set_default(const char *section, const char *name, const char *value);
-extern long long config_set_number(const char *section, const char *name, long long value);
-extern int config_set_boolean(const char *section, const char *name, int value);
+#define config_set(section, name, default_value) appconfig_get(&netdata_config, section, name, default_value)
+#define config_set_default(section, name, value) appconfig_set_default(&netdata_config, section, name, value)
+#define config_set_number(section, name, value) appconfig_set_number(&netdata_config, section, name, value)
+#define config_set_boolean(section, name, value) appconfig_set_boolean(&netdata_config, section, name, value)
 
-extern int config_exists(const char *section, const char *name);
-extern int config_rename(const char *section, const char *old, const char *new);
+#define config_exists(section, name) appconfig_exists(&netdata_config, section, name)
+#define config_move(section_old, name_old, section_new, name_new) appconfig_move(&netdata_config, section_old, name_old, section_new, name_new)
 
-extern void generate_config(BUFFER *wb, int only_changed);
+#define config_generate(buffer, only_changed) appconfig_generate(&netdata_config, buffer, only_changed)
 
 #endif /* NETDATA_CONFIG_H */
index 103bc143293d26c494047ffcfbd890be4c92fc1c..f5c25c7366ecb27364654a8fbd55e39b1d12435d 100644 (file)
@@ -64,6 +64,7 @@ static int
 
 static size_t
         global_iterations_counter = 1,
+        calls_counter = 0,
         file_counter = 0;
 
 
@@ -831,7 +832,7 @@ static inline int read_proc_pid_stat(struct pid_stat *p) {
 
     p->last_stat_collected_usec = p->stat_collected_usec;
     p->stat_collected_usec = now_monotonic_usec();
-    file_counter++;
+    calls_counter++;
 
     // p->pid           = str2pid_t(procfile_lineword(ff, 0, 0));
     char *comm          = procfile_lineword(ff, 0, 1);
@@ -983,7 +984,7 @@ static inline int read_proc_pid_statm(struct pid_stat *p) {
     ff = procfile_readall(ff);
     if(unlikely(!ff)) goto cleanup;
 
-    file_counter++;
+    calls_counter++;
 
     p->statm_size           = str2kernel_uint_t(procfile_lineword(ff, 0, 0));
     p->statm_resident       = str2kernel_uint_t(procfile_lineword(ff, 0, 1));
@@ -1022,7 +1023,7 @@ static inline int read_proc_pid_io(struct pid_stat *p) {
     ff = procfile_readall(ff);
     if(unlikely(!ff)) goto cleanup;
 
-    file_counter++;
+    calls_counter++;
 
     p->last_io_collected_usec = p->io_collected_usec;
     p->io_collected_usec = now_monotonic_usec();
@@ -1098,7 +1099,7 @@ static inline int read_proc_stat() {
     last_collected_usec = collected_usec;
     collected_usec = now_monotonic_usec();
 
-    file_counter++;
+    calls_counter++;
 
     kernel_uint_t last;
 
@@ -1387,8 +1388,9 @@ static inline void make_all_pid_fds_negative(struct pid_stat *p) {
 }
 
 static inline void cleanup_negative_pid_fds(struct pid_stat *p) {
-    int *fd = p->fds, *end = &p->fds[p->fds_size];
-    while(fd < end) {
+    int *fd = p->fds, *fdend = &p->fds[p->fds_size];
+
+    while(fd < fdend) {
         if(unlikely(*fd < 0)) {
             file_descriptor_not_used(-(*fd));
             *fd++ = 0;
@@ -1429,7 +1431,7 @@ static inline int read_pid_file_descriptors(struct pid_stat *p) {
             continue;
 
         // get its number
-        int fdid = (int)str2l(de->d_name);
+        int fdid = (int) str2l(de->d_name);
         if(unlikely(fdid < 0)) continue;
 
         // check if the fds array is small
@@ -1437,7 +1439,12 @@ static inline int read_pid_file_descriptors(struct pid_stat *p) {
             // it is small, extend it
 
             if(unlikely(debug))
-                fprintf(stderr, "apps.plugin: extending fd memory slots for %s from %d to %d\n", p->comm, p->fds_size, fdid + MAX_SPARE_FDS);
+                fprintf(stderr
+                        , "apps.plugin: extending fd memory slots for %s from %d to %d\n"
+                        , p->comm
+                        , p->fds_size
+                        , fdid + MAX_SPARE_FDS
+                );
 
             p->fds = reallocz(p->fds, (fdid + MAX_SPARE_FDS) * sizeof(int));
 
@@ -1468,9 +1475,10 @@ static inline int read_pid_file_descriptors(struct pid_stat *p) {
             p->fds[fdid] = file_descriptor_find_or_add(linkname);
         }
 
-        // else make it positive again, we need it
-        // of course, the actual file may have changed, but we don't care so much
-        // FIXME: we could compare the inode as returned by readdir dirent structure
+            // else make it positive again, we need it
+            // of course, the actual file may have changed, but we don't care so much
+            // FIXME: we could compare the inode as returned by readdir dirent structure
+            // UPDATE: no we cannot use inodes - under /proc inodes don't change when the link is changed
 
         else
             p->fds[fdid] = -p->fds[fdid];
@@ -1952,10 +1960,11 @@ static void cleanup_exited_pids(void) {
             if(unlikely(debug && (p->keep || p->keeploops)))
                 fprintf(stderr, " > CLEANUP cannot keep exited process %d (%s) anymore - removing it.\n", p->pid, p->comm);
 
-            for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] > 0) {
-                file_descriptor_not_used(p->fds[c]);
-                p->fds[c] = 0;
-            }
+            for(c = 0; c < p->fds_size; c++)
+                if(p->fds[c] > 0) {
+                    file_descriptor_not_used(p->fds[c]);
+                    p->fds[c] = 0;
+                }
 
             pid_t r = p->pid;
             p = p->next;
@@ -2398,6 +2407,7 @@ static usec_t send_resource_usage_to_netdata() {
                         "DIMENSION user '' incremental 1 1000\n"
                         "DIMENSION system '' incremental 1 1000\n"
                         "CHART netdata.apps_files '' 'Apps Plugin Files' 'files/s' apps.plugin netdata.apps_files line 140001 %1$d\n"
+                        "DIMENSION calls '' incremental 1 1\n"
                         "DIMENSION files '' incremental 1 1\n"
                         "DIMENSION pids '' absolute 1 1\n"
                         "DIMENSION fds '' absolute 1 1\n"
@@ -2431,6 +2441,7 @@ static usec_t send_resource_usage_to_netdata() {
         "SET system = %llu\n"
         "END\n"
         "BEGIN netdata.apps_files %llu\n"
+        "SET calls = %zu\n"
         "SET files = %zu\n"
         "SET pids = %zu\n"
         "SET fds = %d\n"
@@ -2447,6 +2458,7 @@ static usec_t send_resource_usage_to_netdata() {
         , cpuuser
         , cpusyst
         , usec
+        , calls_counter
         , file_counter
         , all_pids_count
         , all_files_len
@@ -2890,6 +2902,22 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type
 // ----------------------------------------------------------------------------
 // parse command line arguments
 
+int check_proc_1_io() {
+    int ret = 0;
+
+    procfile *ff = procfile_open("/proc/1/io", NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
+    if(!ff) goto cleanup;
+
+    ff = procfile_readall(ff);
+    if(!ff) goto cleanup;
+
+    ret = 1;
+
+cleanup:
+    procfile_close(ff);
+    return ret;
+}
+
 static void parse_args(int argc, char **argv)
 {
     int i, freq = 0;
@@ -2909,6 +2937,15 @@ static void parse_args(int argc, char **argv)
             exit(0);
         }
 
+        if(strcmp("test-permissions", argv[i]) == 0 || strcmp("-t", argv[i]) == 0) {
+            if(!check_proc_1_io()) {
+                perror("Tried to read /proc/1/io and it failed");
+                exit(1);
+            }
+            printf("OK\n");
+            exit(0);
+        }
+
         if(strcmp("debug", argv[i]) == 0) {
             debug = 1;
             // debug_flags = 0xffffffff;
@@ -3081,8 +3118,6 @@ int main(int argc, char **argv) {
     // set the name for logging
     program_name = "apps.plugin";
 
-    info("started on pid %d", getpid());
-
     // disable syslog for apps.plugin
     error_log_syslog = 0;
 
@@ -3124,27 +3159,27 @@ int main(int argc, char **argv) {
 
     parse_args(argc, argv);
 
-    if(!check_capabilities()) {
-        if(!am_i_running_as_root()) {
-            uid_t uid = getuid(), euid = geteuid();
+    if(!check_capabilities() && !am_i_running_as_root() && !check_proc_1_io()) {
+        uid_t uid = getuid(), euid = geteuid();
 #ifdef HAVE_CAPABILITY
-            error("apps.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities. "
-                          "Without these, apps.plugin cannot report disk I/O utilization of other processes. "
-                          "To enable capabilities run: sudo setcap cap_dac_read_search,cap_sys_ptrace+ep %s; "
-                          "To enable setuid to root run: sudo chown root %s; sudo chmod 4755 %s; "
-                  , uid, euid, argv[0], argv[0], argv[0]
-            );
+        error("apps.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities. "
+                      "Without these, apps.plugin cannot report disk I/O utilization of other processes. "
+                      "To enable capabilities run: sudo setcap cap_dac_read_search,cap_sys_ptrace+ep %s; "
+                      "To enable setuid to root run: sudo chown root %s; sudo chmod 4755 %s; "
+              , uid, euid, argv[0], argv[0], argv[0]
+        );
 #else
-            error("apps.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities. "
-                          "Without these, apps.plugin cannot report disk I/O utilization of other processes. "
-                          "Your system does not support capabilities. "
-                          "To enable setuid to root run: sudo chown root %s; sudo chmod 4755 %s; "
-                  , uid, euid, argv[0], argv[0]
-            );
+        error("apps.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities. "
+                      "Without these, apps.plugin cannot report disk I/O utilization of other processes. "
+                      "Your system does not support capabilities. "
+                      "To enable setuid to root run: sudo chown root %s; sudo chmod 4755 %s; "
+              , uid, euid, argv[0], argv[0]
+        );
 #endif
-        }
     }
 
+    info("started on pid %d", getpid());
+
     all_pids_sortlist = callocz(sizeof(pid_t), (size_t)pid_max);
     all_pids          = callocz(sizeof(struct pid_stat *), (size_t) pid_max);
 
index 844c334e8df40637c78fe9ea08d5ebd192d86cbb..d30deef086f4322c6a110198839ff0988e421911 100644 (file)
@@ -1,12 +1,48 @@
 #include "common.h"
 
+// ----------------------------------------------------------------------------
+// How backends work in netdata:
+//
+// 1. There is an independent thread that runs at the required interval
+//    (for example, once every 10 seconds)
+//
+// 2. Every time it wakes, it calls the backend formatting functions to build
+//    a buffer of data. This is a very fast, memory only operation.
+//
+// 3. If the buffer already includes data, the new data are appended.
+//    If the buffer becomes too big, because the data cannot be sent, a
+//    log is written and the buffer is discarded.
+//
+// 4. Then it tries to send all the data. It blocks until all the data are sent
+//    or the socket returns an error.
+//    If the time required for this is above the interval, it starts skipping
+//    intervals, but the calculated values include the entire database, without
+//    gaps (it remembers the timestamps and continues from where it stopped).
+//
+// 5. repeats the above forever.
+//
+
 #define BACKEND_SOURCE_DATA_AS_COLLECTED 0x00000001
 #define BACKEND_SOURCE_DATA_AVERAGE      0x00000002
 #define BACKEND_SOURCE_DATA_SUM          0x00000004
 
-static inline calculated_number backend_calculate_value_from_stored_data(RRDSET *st, RRDDIM *rd, time_t after, time_t before, uint32_t options) {
+
+// ----------------------------------------------------------------------------
+// helper functions for backends
+
+// calculate the SUM or AVERAGE of a dimension, for any timeframe
+// may return NAN if the database does not have any value in the give timeframe
+
+static inline calculated_number backend_calculate_value_from_stored_data(
+          RRDSET *st                // the chart
+        , RRDDIM *rd                // the dimension
+        , time_t after              // the start timestamp
+        , time_t before             // the end timestamp
+        , uint32_t options          // BACKEND_SOURCE_* bitmap
+) {
+    // find the edges of the rrd database for this chart
     time_t first_t = rrdset_first_entry_t(st);
-    time_t last_t = rrdset_last_entry_t(st);
+    time_t last_t  = rrdset_last_entry_t(st);
 
     if(unlikely(before < first_t || after > last_t))
         // the chart has not been updated in the wanted timeframe
@@ -21,7 +57,7 @@ static inline calculated_number backend_calculate_value_from_stored_data(RRDSET
         after = first_t;
 
     if(unlikely(after > before))
-        // this can happen when the st->update_every > before - after
+        // this can happen when st->update_every > before - after
         before = after;
 
     if(unlikely(before > last_t))
@@ -35,14 +71,20 @@ static inline calculated_number backend_calculate_value_from_stored_data(RRDSET
             slot, stop_now = 0;
 
     for(slot = start_at_slot; !stop_now ; slot--) {
+
         if(unlikely(slot < 0)) slot = st->entries - 1;
         if(unlikely(slot == stop_at_slot)) stop_now = 1;
 
         storage_number n = rd->values[slot];
-        if(unlikely(!does_storage_number_exist(n))) continue;
+
+        if(unlikely(!does_storage_number_exist(n))) {
+            // not collected
+            continue;
+        }
 
         calculated_number value = unpack_storage_number(n);
         sum += value;
+
         counter++;
     }
 
@@ -55,84 +97,295 @@ static inline calculated_number backend_calculate_value_from_stored_data(RRDSET
     return sum / (calculated_number)counter;
 }
 
-static inline int format_dimension_collected_graphite_plaintext(BUFFER *b, const char *prefix, RRDHOST *host, const char *hostname, RRDSET *st, RRDDIM *rd, time_t after, time_t before, uint32_t options) {
+
+// discard a response received by a backend
+// after logging a simple of it to error.log
+
+static inline int discard_response(BUFFER *b, const char *backend) {
+    char sample[1024];
+    const char *s = buffer_tostring(b);
+    char *d = sample, *e = &sample[sizeof(sample) - 1];
+
+    for(; *s && d < e ;s++) {
+        char c = *s;
+        if(unlikely(!isprint(c))) c = ' ';
+        *d++ = c;
+    }
+    *d = '\0';
+
+    info("Received %zu bytes from %s backend. Ignoring them. Sample: '%s'", buffer_strlen(b), backend, sample);
+    buffer_flush(b);
+    return 0;
+}
+
+
+// ----------------------------------------------------------------------------
+// graphite backend
+
+static inline int format_dimension_collected_graphite_plaintext(
+          BUFFER *b                 // the buffer to write data to
+        , const char *prefix        // the prefix to use
+        , RRDHOST *host             // the host this chart comes from
+        , const char *hostname      // the hostname (to override host->hostname)
+        , RRDSET *st                // the chart
+        , RRDDIM *rd                // the dimension
+        , time_t after              // the start timestamp
+        , time_t before             // the end timestamp
+        , uint32_t options          // BACKEND_SOURCE_* bitmap
+) {
     (void)host;
     (void)after;
     (void)before;
     (void)options;
-    buffer_sprintf(b, "%s.%s.%s.%s " COLLECTED_NUMBER_FORMAT " %u\n", prefix, hostname, st->id, rd->id, rd->last_collected_value, (uint32_t)rd->last_collected_time.tv_sec);
+
+    buffer_sprintf(
+            b
+            , "%s.%s.%s.%s " COLLECTED_NUMBER_FORMAT " %u\n"
+            , prefix
+            , hostname
+            , st->id
+            , rd->id
+            , rd->last_collected_value
+            , (uint32_t)rd->last_collected_time.tv_sec
+    );
+
     return 1;
 }
 
-static inline int format_dimension_stored_graphite_plaintext(BUFFER *b, const char *prefix, RRDHOST *host, const char *hostname, RRDSET *st, RRDDIM *rd, time_t after, time_t before, uint32_t options) {
+static inline int format_dimension_stored_graphite_plaintext(
+          BUFFER *b                 // the buffer to write data to
+        , const char *prefix        // the prefix to use
+        , RRDHOST *host             // the host this chart comes from
+        , const char *hostname      // the hostname (to override host->hostname)
+        , RRDSET *st                // the chart
+        , RRDDIM *rd                // the dimension
+        , time_t after              // the start timestamp
+        , time_t before             // the end timestamp
+        , uint32_t options          // BACKEND_SOURCE_* bitmap
+) {
     (void)host;
+
     calculated_number value = backend_calculate_value_from_stored_data(st, rd, after, before, options);
+
     if(!isnan(value)) {
-        buffer_sprintf(b, "%s.%s.%s.%s " CALCULATED_NUMBER_FORMAT " %u\n", prefix, hostname, st->id, rd->id, value, (uint32_t) before);
+
+        buffer_sprintf(
+                b
+                , "%s.%s.%s.%s " CALCULATED_NUMBER_FORMAT " %u\n"
+                , prefix
+                , hostname
+                , st->id
+                , rd->id
+                , value
+                , (uint32_t) before
+        );
+
         return 1;
     }
     return 0;
 }
 
-static inline int format_dimension_collected_opentsdb_telnet(BUFFER *b, const char *prefix, RRDHOST *host, const char *hostname, RRDSET *st, RRDDIM *rd, time_t after, time_t before, uint32_t options) {
+static inline int process_graphite_response(BUFFER *b) {
+    return discard_response(b, "graphite");
+}
+
+
+// ----------------------------------------------------------------------------
+// opentsdb backend
+
+static inline int format_dimension_collected_opentsdb_telnet(
+          BUFFER *b                 // the buffer to write data to
+        , const char *prefix        // the prefix to use
+        , RRDHOST *host             // the host this chart comes from
+        , const char *hostname      // the hostname (to override host->hostname)
+        , RRDSET *st                // the chart
+        , RRDDIM *rd                // the dimension
+        , time_t after              // the start timestamp
+        , time_t before             // the end timestamp
+        , uint32_t options          // BACKEND_SOURCE_* bitmap
+) {
     (void)host;
     (void)after;
     (void)before;
     (void)options;
-    buffer_sprintf(b, "put %s.%s.%s %u " COLLECTED_NUMBER_FORMAT " host=%s\n", prefix, st->id, rd->id, (uint32_t)rd->last_collected_time.tv_sec, rd->last_collected_value, hostname);
+
+    buffer_sprintf(
+            b
+            , "put %s.%s.%s %u " COLLECTED_NUMBER_FORMAT " host=%s\n"
+            , prefix
+            , st->id
+            , rd->id
+            , (uint32_t)rd->last_collected_time.tv_sec
+            , rd->last_collected_value
+            , hostname
+    );
+
     return 1;
 }
 
-static inline int format_dimension_stored_opentsdb_telnet(BUFFER *b, const char *prefix, RRDHOST *host, const char *hostname, RRDSET *st, RRDDIM *rd, time_t after, time_t before, uint32_t options) {
+static inline int format_dimension_stored_opentsdb_telnet(
+          BUFFER *b                 // the buffer to write data to
+        , const char *prefix        // the prefix to use
+        , RRDHOST *host             // the host this chart comes from
+        , const char *hostname      // the hostname (to override host->hostname)
+        , RRDSET *st                // the chart
+        , RRDDIM *rd                // the dimension
+        , time_t after              // the start timestamp
+        , time_t before             // the end timestamp
+        , uint32_t options          // BACKEND_SOURCE_* bitmap
+) {
     (void)host;
+
     calculated_number value = backend_calculate_value_from_stored_data(st, rd, after, before, options);
+
     if(!isnan(value)) {
-        buffer_sprintf(b, "put %s.%s.%s %u " CALCULATED_NUMBER_FORMAT " host=%s\n", prefix, st->id, rd->id, (uint32_t) before, value, hostname);
+
+        buffer_sprintf(
+                b
+                , "put %s.%s.%s %u " CALCULATED_NUMBER_FORMAT " host=%s\n"
+                , prefix
+                , st->id
+                , rd->id
+                , (uint32_t) before
+                , value
+                , hostname
+        );
+
         return 1;
     }
     return 0;
 }
 
-static inline int process_graphite_response(BUFFER *b) {
-    char sample[1024];
-    const char *s = buffer_tostring(b);
-    char *d = sample, *e = &sample[sizeof(sample) - 1];
+static inline int process_opentsdb_response(BUFFER *b) {
+    return discard_response(b, "opentsdb");
+}
 
-    for(; *s && d < e ;s++) {
-        char c = *s;
-        if(unlikely(!isprint(c))) c = ' ';
-        *d++ = c;
-    }
-    *d = '\0';
 
-    info("Received %zu bytes from graphite backend. Ignoring them. Sample: '%s'", buffer_strlen(b), sample);
-    buffer_flush(b);
-    return 0;
+// ----------------------------------------------------------------------------
+// json backend
+
+static inline int format_dimension_collected_json_plaintext(
+          BUFFER *b                 // the buffer to write data to
+        , const char *prefix        // the prefix to use
+        , RRDHOST *host             // the host this chart comes from
+        , const char *hostname      // the hostname (to override host->hostname)
+        , RRDSET *st                // the chart
+        , RRDDIM *rd                // the dimension
+        , time_t after              // the start timestamp
+        , time_t before             // the end timestamp
+        , uint32_t options          // BACKEND_SOURCE_* bitmap
+) {
+    (void)host;
+    (void)after;
+    (void)before;
+    (void)options;
+
+    buffer_sprintf(b, "{"
+        "\"prefix\":\"%s\","
+        "\"hostname\":\"%s\","
+
+        "\"chart_id\":\"%s\","
+        "\"chart_name\":\"%s\","
+        "\"family\":\"%s\","
+        "\"context\": \"%s\","
+        "\"type\":\"%s\","
+        "\"units\": \"%s\","
+
+        "\"id\":\"%s\","
+        "\"name\":\"%s\","
+        "\"value\":" COLLECTED_NUMBER_FORMAT ","
+
+        "\"timestamp\": %u}\n", 
+            prefix,
+            hostname,
+
+            st->id,
+            st->name,
+            st->family,
+            st->context,
+            st->type,
+            st->units,
+
+            rd->id,
+            rd->name,
+            rd->last_collected_value,
+
+            (uint32_t)rd->last_collected_time.tv_sec
+    );
+
+    return 1;
 }
 
-static inline int process_opentsdb_response(BUFFER *b) {
-    char sample[1024];
-    const char *s = buffer_tostring(b);
-    char *d = sample, *e = &sample[sizeof(sample) - 1];
+static inline int format_dimension_stored_json_plaintext(
+          BUFFER *b                 // the buffer to write data to
+        , const char *prefix        // the prefix to use
+        , RRDHOST *host             // the host this chart comes from
+        , const char *hostname      // the hostname (to override host->hostname)
+        , RRDSET *st                // the chart
+        , RRDDIM *rd                // the dimension
+        , time_t after              // the start timestamp
+        , time_t before             // the end timestamp
+        , uint32_t options          // BACKEND_SOURCE_* bitmap
+) {
+    (void)host;
 
-    for(; *s && d < e ;s++) {
-        char c = *s;
-        if(unlikely(!isprint(c))) c = ' ';
-        *d++ = c;
-    }
-    *d = '\0';
+    calculated_number value = backend_calculate_value_from_stored_data(st, rd, after, before, options);
 
-    info("Received %zu bytes from opentsdb backend. Ignoring them. Sample: '%s'", buffer_strlen(b), sample);
-    buffer_flush(b);
+    if(!isnan(value)) {
+        buffer_sprintf(b, "{"
+            "\"prefix\":\"%s\","
+            "\"hostname\":\"%s\","
+
+            "\"chart_id\":\"%s\","
+            "\"chart_name\":\"%s\","
+            "\"family\":\"%s\","
+            "\"context\": \"%s\","
+            "\"type\":\"%s\","
+            "\"units\": \"%s\","
+
+            "\"id\":\"%s\","
+            "\"name\":\"%s\","
+            "\"value\":" CALCULATED_NUMBER_FORMAT ","
+
+            "\"timestamp\": %u}\n", 
+                prefix,
+                hostname,
+                
+                st->id,
+                st->name,
+                st->family,
+                st->context,
+                st->type,
+                st->units,
+
+                rd->id,
+                rd->name,
+                value, 
+                
+                (uint32_t)before
+        );
+        
+        return 1;
+    }
     return 0;
 }
 
+static inline int process_json_response(BUFFER *b) {
+    return discard_response(b, "json");
+}
+
+
+// ----------------------------------------------------------------------------
+// the backend thread
+
 void *backends_main(void *ptr) {
+    int default_port = 0;
+    int sock = -1;
     struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
 
     BUFFER *b = buffer_create(1), *response = buffer_create(1);
-    int (*backend_request_formatter)(BUFFER *b, const char *prefix, RRDHOST *host, const char *hostname, RRDSET *st, RRDDIM *rd, time_t after, time_t before, uint32_t options) = NULL;
-    int (*backend_response_checker)(BUFFER *b) = NULL;
+    int (*backend_request_formatter)(BUFFER *, const char *, RRDHOST *, const char *, RRDSET *, RRDDIM *, time_t, time_t, uint32_t) = NULL;
+    int (*backend_response_checker)(BUFFER *) = NULL;
 
     info("BACKEND thread created with task id %d", gettid());
 
@@ -149,22 +402,21 @@ void *backends_main(void *ptr) {
             .tv_sec = 0,
             .tv_usec = 0
     };
-    int default_port = 0;
-    int sock = -1;
     uint32_t options;
-    int enabled = config_get_boolean("backend", "enabled", 0);
-    const char *source = config_get("backend", "data source", "average");
-    const char *type = config_get("backend", "type", "graphite");
-    const char *destination = config_get("backend", "destination", "localhost");
-    const char *prefix = config_get("backend", "prefix", "netdata");
-    const char *hostname = config_get("backend", "hostname", localhost.hostname);
-    int frequency = (int)config_get_number("backend", "update every", 10);
-    int buffer_on_failures = (int)config_get_number("backend", "buffer on failures", 10);
-    long timeoutms = config_get_number("backend", "timeout ms", frequency * 2 * 1000);
+    int enabled             = config_get_boolean(CONFIG_SECTION_BACKEND, "enabled", 0);
+    const char *source      = config_get(CONFIG_SECTION_BACKEND, "data source", "average");
+    const char *type        = config_get(CONFIG_SECTION_BACKEND, "type", "graphite");
+    const char *destination = config_get(CONFIG_SECTION_BACKEND, "destination", "localhost");
+    const char *prefix      = config_get(CONFIG_SECTION_BACKEND, "prefix", "netdata");
+    const char *hostname    = config_get(CONFIG_SECTION_BACKEND, "hostname", localhost->hostname);
+    int frequency           = (int)config_get_number(CONFIG_SECTION_BACKEND, "update every", 10);
+    int buffer_on_failures  = (int)config_get_number(CONFIG_SECTION_BACKEND, "buffer on failures", 10);
+    long timeoutms          = config_get_number(CONFIG_SECTION_BACKEND, "timeout ms", frequency * 2 * 1000);
 
     // ------------------------------------------------------------------------
     // validate configuration options
     // and prepare for sending data to our backend
+
     if(!enabled || frequency < 1)
         goto cleanup;
 
@@ -182,23 +434,49 @@ void *backends_main(void *ptr) {
         goto cleanup;
     }
 
+    if(timeoutms < 1) {
+        error("BACKED invalid timeout %ld ms given. Assuming %d ms.", timeoutms, frequency * 2 * 1000);
+        timeoutms = frequency * 2 * 1000;
+    }
+    timeout.tv_sec  = (timeoutms * 1000) / 1000000;
+    timeout.tv_usec = (timeoutms * 1000) % 1000000;
+
+
+    // ------------------------------------------------------------------------
+    // select the backend type
+
     if(!strcmp(type, "graphite") || !strcmp(type, "graphite:plaintext")) {
+
         default_port = 2003;
+        backend_response_checker = process_graphite_response;
+
         if(options == BACKEND_SOURCE_DATA_AS_COLLECTED)
             backend_request_formatter = format_dimension_collected_graphite_plaintext;
         else
             backend_request_formatter = format_dimension_stored_graphite_plaintext;
 
-        backend_response_checker = process_graphite_response;
     }
     else if(!strcmp(type, "opentsdb") || !strcmp(type, "opentsdb:telnet")) {
+
         default_port = 4242;
+        backend_response_checker = process_opentsdb_response;
+
         if(options == BACKEND_SOURCE_DATA_AS_COLLECTED)
             backend_request_formatter = format_dimension_collected_opentsdb_telnet;
         else
             backend_request_formatter = format_dimension_stored_opentsdb_telnet;
 
-        backend_response_checker = process_opentsdb_response;
+    }
+    else if (!strcmp(type, "json") || !strcmp(type, "json:plaintext")) {
+
+        default_port = 5448;
+        backend_response_checker = process_json_response;
+
+        if (options == BACKEND_SOURCE_DATA_AS_COLLECTED)
+            backend_request_formatter = format_dimension_collected_json_plaintext;
+        else
+            backend_request_formatter = format_dimension_stored_json_plaintext;
+
     }
     else {
         error("Unknown backend type '%s'", type);
@@ -210,15 +488,9 @@ void *backends_main(void *ptr) {
         goto cleanup;
     }
 
-    if(timeoutms < 1) {
-        error("BACKED invalid timeout %ld ms given. Assuming %d ms.", timeoutms, frequency * 2 * 1000);
-        timeoutms = frequency * 2 * 1000;
-    }
-    timeout.tv_sec  = (timeoutms * 1000) / 1000000;
-    timeout.tv_usec = (timeoutms * 1000) % 1000000;
 
     // ------------------------------------------------------------------------
-    // prepare the charts for monitoring the backend
+    // prepare the charts for monitoring the backend operation
 
     struct rusage thread;
 
@@ -237,32 +509,23 @@ void *backends_main(void *ptr) {
             chart_backend_reconnects = 0,
             chart_backend_latency = 0;
 
-    RRDSET *chart_metrics = rrdset_find("netdata.backend_metrics");
-    if(!chart_metrics) {
-        chart_metrics = rrdset_create("netdata", "backend_metrics", NULL, "backend", NULL, "Netdata Buffered Metrics", "metrics", 130600, frequency, RRDSET_TYPE_LINE);
-        rrddim_add(chart_metrics, "buffered", NULL,  1, 1, RRDDIM_ABSOLUTE);
-        rrddim_add(chart_metrics, "lost",     NULL,  1, 1, RRDDIM_ABSOLUTE);
-        rrddim_add(chart_metrics, "sent",     NULL,  1, 1, RRDDIM_ABSOLUTE);
-    }
+    RRDSET *chart_metrics = rrdset_create_localhost("netdata", "backend_metrics", NULL, "backend", NULL, "Netdata Buffered Metrics", "metrics", 130600, frequency, RRDSET_TYPE_LINE);
+    rrddim_add(chart_metrics, "buffered", NULL,  1, 1, RRD_ALGORITHM_ABSOLUTE);
+    rrddim_add(chart_metrics, "lost",     NULL,  1, 1, RRD_ALGORITHM_ABSOLUTE);
+    rrddim_add(chart_metrics, "sent",     NULL,  1, 1, RRD_ALGORITHM_ABSOLUTE);
 
-    RRDSET *chart_bytes = rrdset_find("netdata.backend_bytes");
-    if(!chart_bytes) {
-        chart_bytes = rrdset_create("netdata", "backend_bytes", NULL, "backend", NULL, "Netdata Backend Data Size", "KB", 130610, frequency, RRDSET_TYPE_AREA);
-        rrddim_add(chart_bytes, "buffered", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-        rrddim_add(chart_bytes, "lost",     NULL, 1, 1024, RRDDIM_ABSOLUTE);
-        rrddim_add(chart_bytes, "sent",     NULL, 1, 1024, RRDDIM_ABSOLUTE);
-        rrddim_add(chart_bytes, "received", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-    }
+    RRDSET *chart_bytes = rrdset_create_localhost("netdata", "backend_bytes", NULL, "backend", NULL, "Netdata Backend Data Size", "KB", 130610, frequency, RRDSET_TYPE_AREA);
+    rrddim_add(chart_bytes, "buffered", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
+    rrddim_add(chart_bytes, "lost",     NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
+    rrddim_add(chart_bytes, "sent",     NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
+    rrddim_add(chart_bytes, "received", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
 
-    RRDSET *chart_ops = rrdset_find("netdata.backend_ops");
-    if(!chart_ops) {
-        chart_ops = rrdset_create("netdata", "backend_ops", NULL, "backend", NULL, "Netdata Backend Operations", "operations", 130630, frequency, RRDSET_TYPE_LINE);
-        rrddim_add(chart_ops, "write",     NULL, 1, 1, RRDDIM_ABSOLUTE);
-        rrddim_add(chart_ops, "discard",   NULL, 1, 1, RRDDIM_ABSOLUTE);
-        rrddim_add(chart_ops, "reconnect", NULL, 1, 1, RRDDIM_ABSOLUTE);
-        rrddim_add(chart_ops, "failure",   NULL, 1, 1, RRDDIM_ABSOLUTE);
-        rrddim_add(chart_ops, "read",      NULL, 1, 1, RRDDIM_ABSOLUTE);
-    }
+    RRDSET *chart_ops = rrdset_create_localhost("netdata", "backend_ops", NULL, "backend", NULL, "Netdata Backend Operations", "operations", 130630, frequency, RRDSET_TYPE_LINE);
+    rrddim_add(chart_ops, "write",     NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+    rrddim_add(chart_ops, "discard",   NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+    rrddim_add(chart_ops, "reconnect", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+    rrddim_add(chart_ops, "failure",   NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+    rrddim_add(chart_ops, "read",      NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
 
     /*
      * this is misleading - we can only measure the time we need to send data
@@ -271,19 +534,14 @@ void *backends_main(void *ptr) {
      *
      * issue #1432 and https://www.softlab.ntua.gr/facilities/documentation/unix/unix-socket-faq/unix-socket-faq-2.html
      *
-    RRDSET *chart_latency = rrdset_find("netdata.backend_latency");
-    if(!chart_latency) {
-        chart_latency = rrdset_create("netdata", "backend_latency", NULL, "backend", NULL, "Netdata Backend Latency", "ms", 130620, frequency, RRDSET_TYPE_AREA);
-        rrddim_add(chart_latency, "latency",   NULL,  1, 1000, RRDDIM_ABSOLUTE);
-    }
+    RRDSET *chart_latency = rrdset_create_localhost("netdata", "backend_latency", NULL, "backend", NULL, "Netdata Backend Latency", "ms", 130620, frequency, RRDSET_TYPE_AREA);
+    rrddim_add(chart_latency, "latency",   NULL,  1, 1000, RRD_ALGORITHM_ABSOLUTE);
     */
 
-    RRDSET *chart_rusage = rrdset_find("netdata.backend_thread_cpu");
-    if(!chart_rusage) {
-        chart_rusage = rrdset_create("netdata", "backend_thread_cpu", NULL, "backend", NULL, "NetData Backend Thread CPU usage", "milliseconds/s", 130630, frequency, RRDSET_TYPE_STACKED);
-        rrddim_add(chart_rusage, "user",   NULL, 1, 1000, RRDDIM_INCREMENTAL);
-        rrddim_add(chart_rusage, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);
-    }
+    RRDSET *chart_rusage = rrdset_create_localhost("netdata", "backend_thread_cpu", NULL, "backend", NULL, "NetData Backend Thread CPU usage", "milliseconds/s", 130630, frequency, RRDSET_TYPE_STACKED);
+    rrddim_add(chart_rusage, "user",   NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL);
+    rrddim_add(chart_rusage, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL);
+
 
     // ------------------------------------------------------------------------
     // prepare the backend main loop
@@ -297,32 +555,43 @@ void *backends_main(void *ptr) {
     heartbeat_init(&hb);
 
     for(;;) {
+
         // ------------------------------------------------------------------------
         // Wait for the next iteration point.
         heartbeat_next(&hb, step_ut);
         time_t before = now_realtime_sec();
 
+
         // ------------------------------------------------------------------------
         // add to the buffer the data we need to send to the backend
-        RRDSET *st;
+
         int pthreadoldcancelstate;
 
         if(unlikely(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &pthreadoldcancelstate) != 0))
             error("Cannot set pthread cancel state to DISABLE.");
 
-        rrdhost_rdlock(&localhost);
-        for(st = localhost.rrdset_root; st ;st = st->next) {
-            pthread_rwlock_rdlock(&st->rwlock);
+        rrd_rdlock();
+        RRDHOST *host;
+        rrdhost_foreach_read(host) {
+            if(host->rrd_memory_mode == RRD_MEMORY_MODE_NONE)
+                continue;
 
-            RRDDIM *rd;
-            for(rd = st->dimensions; rd ;rd = rd->next) {
-                if(rd->last_collected_time.tv_sec >= after)
-                    chart_buffered_metrics += backend_request_formatter(b, prefix, &localhost, hostname, st, rd, after, before, options);
-            }
+            rrdhost_rdlock(host);
 
-            pthread_rwlock_unlock(&st->rwlock);
+            RRDSET *st;
+            rrdset_foreach_read(st, host) {
+                rrdset_rdlock(st);
+
+                RRDDIM *rd;
+                rrddim_foreach_read(rd, st) {
+                    if(rd->last_collected_time.tv_sec >= after)
+                        chart_buffered_metrics += backend_request_formatter(b, prefix, host, (host == localhost)?hostname:host->hostname, st, rd, after, before, options);
+                }
+                rrdset_unlock(st);
+            }
+            rrdhost_unlock(host);
         }
-        rrdhost_unlock(&localhost);
+        rrd_unlock();
 
         if(unlikely(pthread_setcancelstate(pthreadoldcancelstate, NULL) != 0))
             error("Cannot set pthread cancel state to RESTORE (%d).", pthreadoldcancelstate);
@@ -392,26 +661,11 @@ void *backends_main(void *ptr) {
 
         if(unlikely(sock == -1)) {
             usec_t start_ut = now_monotonic_usec();
-            const char *s = destination;
-            while(*s) {
-                const char *e = s;
-
-                // skip separators, moving both s(tart) and e(nd)
-                while(isspace(*e) || *e == ',') s = ++e;
+            size_t reconnects = 0;
 
-                // move e(nd) to the first separator
-                while(*e && !isspace(*e) && *e != ',') e++;
+            sock = connect_to_one_of(destination, default_port, &timeout, &reconnects, NULL, 0);
 
-                // is there anything?
-                if(!*s || s == e) break;
-
-                char buf[e - s + 1];
-                strncpyz(buf, s, e - s);
-                chart_backend_reconnects++;
-                sock = connect_to(buf, default_port, &timeout);
-                if(sock != -1) break;
-                s = e;
-            }
+            chart_backend_reconnects += reconnects;
             chart_backend_latency += now_monotonic_usec() - start_ut;
         }
 
@@ -481,7 +735,7 @@ void *backends_main(void *ptr) {
         // ------------------------------------------------------------------------
         // update the monitoring charts
 
-        if(chart_ops->counter_done) rrdset_next(chart_ops);
+        if(likely(chart_ops->counter_done)) rrdset_next(chart_ops);
         rrddim_set(chart_ops, "read",         chart_receptions);
         rrddim_set(chart_ops, "write",        chart_transmission_successes);
         rrddim_set(chart_ops, "discard",      chart_data_lost_events);
@@ -489,13 +743,13 @@ void *backends_main(void *ptr) {
         rrddim_set(chart_ops, "reconnect",    chart_backend_reconnects);
         rrdset_done(chart_ops);
 
-        if(chart_metrics->counter_done) rrdset_next(chart_metrics);
+        if(likely(chart_metrics->counter_done)) rrdset_next(chart_metrics);
         rrddim_set(chart_metrics, "buffered", chart_buffered_metrics);
         rrddim_set(chart_metrics, "lost",     chart_lost_metrics);
         rrddim_set(chart_metrics, "sent",     chart_sent_metrics);
         rrdset_done(chart_metrics);
 
-        if(chart_bytes->counter_done) rrdset_next(chart_bytes);
+        if(likely(chart_bytes->counter_done)) rrdset_next(chart_bytes);
         rrddim_set(chart_bytes, "buffered",   chart_buffered_bytes);
         rrddim_set(chart_bytes, "lost",       chart_lost_bytes);
         rrddim_set(chart_bytes, "sent",       chart_sent_bytes);
@@ -503,13 +757,13 @@ void *backends_main(void *ptr) {
         rrdset_done(chart_bytes);
 
         /*
-        if(chart_latency->counter_done) rrdset_next(chart_latency);
+        if(likely(chart_latency->counter_done)) rrdset_next(chart_latency);
         rrddim_set(chart_latency, "latency",  chart_backend_latency);
         rrdset_done(chart_latency);
         */
 
         getrusage(RUSAGE_THREAD, &thread);
-        if(chart_rusage->counter_done) rrdset_next(chart_rusage);
+        if(likely(chart_rusage->counter_done)) rrdset_next(chart_rusage);
         rrddim_set(chart_rusage, "user",   thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec);
         rrddim_set(chart_rusage, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec);
         rrdset_done(chart_rusage);
index 88d3783e8e6c6233ea26ce17617b76a352890014..239b36e525a1ef894474681dea857146eb30443b 100644 (file)
@@ -74,6 +74,14 @@ inline usec_t timeval_usec(struct timeval *tv) {
     return (usec_t)tv->tv_sec * USEC_PER_SEC + tv->tv_usec;
 }
 
+inline susec_t dt_usec_signed(struct timeval *now, struct timeval *old) {
+    usec_t ts1 = timeval_usec(now);
+    usec_t ts2 = timeval_usec(old);
+
+    if(likely(ts1 >= ts2)) return (susec_t)(ts1 - ts2);
+    return -((susec_t)(ts2 - ts1));
+}
+
 inline usec_t dt_usec(struct timeval *now, struct timeval *old) {
     usec_t ts1 = timeval_usec(now);
     usec_t ts2 = timeval_usec(old);
index 895f09ab4dbeac2b7caba20fed887a9df6b69b88..c410a0521661e329a0751789379bf1487534a79d 100644 (file)
@@ -13,6 +13,7 @@ typedef int clockid_t;
 #endif
 
 typedef unsigned long long usec_t;
+typedef long long susec_t;
 
 typedef usec_t heartbeat_t;
 
@@ -94,6 +95,7 @@ extern usec_t now_boottime_usec(void);
 
 extern usec_t timeval_usec(struct timeval *ts);
 extern usec_t dt_usec(struct timeval *now, struct timeval *old);
+extern susec_t dt_usec_signed(struct timeval *now, struct timeval *old);
 
 extern void heartbeat_init(heartbeat_t *hb);
 
index 5243e40739880ba4c980857ba6cda184a66d90c3..e46397c624be004daa578e0539563b90bd5985e8 100644 (file)
@@ -8,6 +8,7 @@
 #    define MADV_DONTFORK INHERIT_NONE
 #endif /* __FreeBSD__ || __APPLE__*/
 
+char *netdata_configured_hostname    = NULL;
 char *netdata_configured_config_dir  = NULL;
 char *netdata_configured_log_dir     = NULL;
 char *netdata_configured_plugins_dir = NULL;
index 5b9b65b034e905d74649e77227e1994b70ce1653..3d864f4244d6b31e2f1499ae2023baf432d601ce 100644 (file)
 #define NETDATA_OS_TYPE "linux"
 #endif /* __FreeBSD__, __APPLE__*/
 
-#include "plugin_tc.h"
-#include "plugins_d.h"
 #include "socket.h"
 #include "eval.h"
 #include "health.h"
 #include "rrd.h"
+#include "plugin_tc.h"
+#include "plugins_d.h"
 #include "rrd2json.h"
 #include "web_client.h"
 #include "web_server.h"
 #include "backends.h"
 #include "inlined.h"
 #include "adaptive_resortable_list.h"
+#include "rrdpush.h"
+#include "web_api_v1.h"
+#include "web_api_old.h"
 
+extern char *netdata_configured_hostname;
 extern char *netdata_configured_config_dir;
 extern char *netdata_configured_log_dir;
 extern char *netdata_configured_plugins_dir;
index e7f3b037fd73bd808ea5d999bb00bd1b60b107ff..42b04c40190b76bd502a73bd5fced3746320aaf7 100644 (file)
@@ -27,7 +27,7 @@ void sig_handler_save(int signo)
     if(signo) {
         error_log_limit_unlimited();
         info("Received signal %d to save the database...", signo);
-        rrdset_save_all();
+        rrdhost_save_all();
         error_log_limit_reset();
     }
 }
@@ -156,7 +156,7 @@ int become_user(const char *username, int pid_fd)
 }
 
 static void oom_score_adj(void) {
-    int score = (int)config_get_number("global", "OOM score", 1000);
+    int score = (int)config_get_number(CONFIG_SECTION_GLOBAL, "OOM score", 1000);
 
     int done = 0;
     int fd = open("/proc/self/oom_score_adj", O_WRONLY);
@@ -175,7 +175,7 @@ static void oom_score_adj(void) {
 
 static void process_nice_level(void) {
 #ifdef HAVE_NICE
-    int nice_level = (int)config_get_number("global", "process nice level", 19);
+    int nice_level = (int)config_get_number(CONFIG_SECTION_GLOBAL, "process nice level", 19);
     if(nice(nice_level) == -1) error("Cannot set netdata CPU nice level to %d.", nice_level);
     else debug(D_SYSTEM, "Set netdata nice level to %d.", nice_level);
 #endif // HAVE_NICE
@@ -239,7 +239,7 @@ static void sched_setscheduler_set(void) {
         int found = 0;
 
         // read the configuration
-        name = config_get("global", "process scheduling policy", name);
+        name = config_get(CONFIG_SECTION_GLOBAL, "process scheduling policy", name);
         int i;
         for(i = 0 ; scheduler_defaults[i].name ; i++) {
             if(!strcmp(name, scheduler_defaults[i].name)) {
@@ -251,7 +251,7 @@ static void sched_setscheduler_set(void) {
                     return;
 
                 if(flags & SCHED_FLAG_PRIORITY_CONFIGURABLE)
-                    priority = (int)config_get_number("global", "process scheduling priority", priority);
+                    priority = (int)config_get_number(CONFIG_SECTION_GLOBAL, "process scheduling priority", priority);
 
 #ifdef HAVE_SCHED_GET_PRIORITY_MIN
                 if(priority < sched_get_priority_min(policy)) {
index 4ad0d5e177dfb2633f72843cedd4823d46c9273e..3fed51bb2ae160c061c0620a260bed3293e8600d 100644 (file)
@@ -93,10 +93,10 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
         do_tcp_packets          = config_get_boolean("plugin:freebsd:sysctl", "ipv4 TCP packets", 1);
         do_tcp_errors           = config_get_boolean("plugin:freebsd:sysctl", "ipv4 TCP errors", 1);
         do_tcp_handshake        = config_get_boolean("plugin:freebsd:sysctl", "ipv4 TCP handshake issues", 1);
-        do_ecn                  = config_get_boolean_ondemand("plugin:freebsd:sysctl", "ECN packets", CONFIG_ONDEMAND_ONDEMAND);
-        do_tcpext_syscookies    = config_get_boolean_ondemand("plugin:freebsd:sysctl", "TCP SYN cookies", CONFIG_ONDEMAND_ONDEMAND);
-        do_tcpext_ofo           = config_get_boolean_ondemand("plugin:freebsd:sysctl", "TCP out-of-order queue", CONFIG_ONDEMAND_ONDEMAND);
-        do_tcpext_connaborts    = config_get_boolean_ondemand("plugin:freebsd:sysctl", "TCP connection aborts", CONFIG_ONDEMAND_ONDEMAND);
+        do_ecn                  = config_get_boolean_ondemand("plugin:freebsd:sysctl", "ECN packets", CONFIG_BOOLEAN_AUTO);
+        do_tcpext_syscookies    = config_get_boolean_ondemand("plugin:freebsd:sysctl", "TCP SYN cookies", CONFIG_BOOLEAN_AUTO);
+        do_tcpext_ofo           = config_get_boolean_ondemand("plugin:freebsd:sysctl", "TCP out-of-order queue", CONFIG_BOOLEAN_AUTO);
+        do_tcpext_connaborts    = config_get_boolean_ondemand("plugin:freebsd:sysctl", "TCP connection aborts", CONFIG_BOOLEAN_AUTO);
         do_udp_packets          = config_get_boolean("plugin:freebsd:sysctl", "ipv4 UDP packets", 1);
         do_udp_errors           = config_get_boolean("plugin:freebsd:sysctl", "ipv4 UDP errors", 1);
         do_icmp_packets         = config_get_boolean("plugin:freebsd:sysctl", "ipv4 ICMP packets", 1);
@@ -105,17 +105,17 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
         do_ip_fragsout          = config_get_boolean("plugin:freebsd:sysctl", "ipv4 fragments sent", 1);
         do_ip_fragsin           = config_get_boolean("plugin:freebsd:sysctl", "ipv4 fragments assembly", 1);
         do_ip_errors            = config_get_boolean("plugin:freebsd:sysctl", "ipv4 errors", 1);
-        do_ip6_packets          = config_get_boolean_ondemand("plugin:freebsd:sysctl", "ipv6 packets", CONFIG_ONDEMAND_ONDEMAND);
-        do_ip6_fragsout         = config_get_boolean_ondemand("plugin:freebsd:sysctl", "ipv6 fragments sent", CONFIG_ONDEMAND_ONDEMAND);
-        do_ip6_fragsin          = config_get_boolean_ondemand("plugin:freebsd:sysctl", "ipv6 fragments assembly", CONFIG_ONDEMAND_ONDEMAND);
-        do_ip6_errors           = config_get_boolean_ondemand("plugin:freebsd:sysctl", "ipv6 errors", CONFIG_ONDEMAND_ONDEMAND);
-        do_icmp6                = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp", CONFIG_ONDEMAND_ONDEMAND);
-        do_icmp6_redir          = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp redirects", CONFIG_ONDEMAND_ONDEMAND);
-        do_icmp6_errors         = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp errors", CONFIG_ONDEMAND_ONDEMAND);
-        do_icmp6_echos          = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp echos", CONFIG_ONDEMAND_ONDEMAND);
-        do_icmp6_router         = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp router", CONFIG_ONDEMAND_ONDEMAND);
-        do_icmp6_neighbor       = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp neighbor", CONFIG_ONDEMAND_ONDEMAND);
-        do_icmp6_types          = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp types", CONFIG_ONDEMAND_ONDEMAND);
+        do_ip6_packets          = config_get_boolean_ondemand("plugin:freebsd:sysctl", "ipv6 packets", CONFIG_BOOLEAN_AUTO);
+        do_ip6_fragsout         = config_get_boolean_ondemand("plugin:freebsd:sysctl", "ipv6 fragments sent", CONFIG_BOOLEAN_AUTO);
+        do_ip6_fragsin          = config_get_boolean_ondemand("plugin:freebsd:sysctl", "ipv6 fragments assembly", CONFIG_BOOLEAN_AUTO);
+        do_ip6_errors           = config_get_boolean_ondemand("plugin:freebsd:sysctl", "ipv6 errors", CONFIG_BOOLEAN_AUTO);
+        do_icmp6                = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp", CONFIG_BOOLEAN_AUTO);
+        do_icmp6_redir          = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp redirects", CONFIG_BOOLEAN_AUTO);
+        do_icmp6_errors         = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp errors", CONFIG_BOOLEAN_AUTO);
+        do_icmp6_echos          = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp echos", CONFIG_BOOLEAN_AUTO);
+        do_icmp6_router         = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp router", CONFIG_BOOLEAN_AUTO);
+        do_icmp6_neighbor       = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp neighbor", CONFIG_BOOLEAN_AUTO);
+        do_icmp6_types          = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp types", CONFIG_BOOLEAN_AUTO);
         do_space                = config_get_boolean("plugin:freebsd:sysctl", "space usage for all disks", 1);
         do_inodes               = config_get_boolean("plugin:freebsd:sysctl", "inodes usage for all disks", 1);
         do_uptime               = config_get_boolean("plugin:freebsd:sysctl", "system uptime", 1);
@@ -285,12 +285,12 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
                 error("DISABLED: system.load");
             } else {
 
-                st = rrdset_find_bytype("system", "load");
+                st = rrdset_find_bytype_localhost("system", "load");
                 if (unlikely(!st)) {
-                    st = rrdset_create("system", "load", NULL, "load", NULL, "System Load Average", "load", 100, (update_every < MIN_LOADAVG_UPDATE_EVERY) ? MIN_LOADAVG_UPDATE_EVERY : update_every, RRDSET_TYPE_LINE);
-                    rrddim_add(st, "load1", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-                    rrddim_add(st, "load5", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-                    rrddim_add(st, "load15", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+                    st = rrdset_create_localhost("system", "load", NULL, "load", NULL, "System Load Average", "load", 100, (update_every < MIN_LOADAVG_UPDATE_EVERY) ? MIN_LOADAVG_UPDATE_EVERY : update_every, RRDSET_TYPE_LINE);
+                    rrddim_add(st, "load1", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
+                    rrddim_add(st, "load5", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
+                    rrddim_add(st, "load15", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
                 }
                 else rrdset_next(st);
 
@@ -318,10 +318,10 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
         } else {
             if (likely(do_all_processes)) {
 
-                st = rrdset_find_bytype("system", "active_processes");
+                st = rrdset_find_bytype_localhost("system", "active_processes");
                 if (unlikely(!st)) {
-                    st = rrdset_create("system", "active_processes", NULL, "processes", NULL, "System Active Processes", "processes", 750, update_every, RRDSET_TYPE_LINE);
-                    rrddim_add(st, "active", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                    st = rrdset_create_localhost("system", "active_processes", NULL, "processes", NULL, "System Active Processes", "processes", 750, update_every, RRDSET_TYPE_LINE);
+                    rrddim_add(st, "active", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
                 }
                 else rrdset_next(st);
 
@@ -333,12 +333,12 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
             if (likely(do_processes)) {
 
-                st = rrdset_find_bytype("system", "processes");
+                st = rrdset_find_bytype_localhost("system", "processes");
                 if (unlikely(!st)) {
-                    st = rrdset_create("system", "processes", NULL, "processes", NULL, "System Processes", "processes", 600, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("system", "processes", NULL, "processes", NULL, "System Processes", "processes", 600, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "running", NULL, 1, 1, RRDDIM_ABSOLUTE);
-                    rrddim_add(st, "blocked", NULL, -1, 1, RRDDIM_ABSOLUTE);
+                    rrddim_add(st, "running", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+                    rrddim_add(st, "blocked", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE);
                 }
                 else rrdset_next(st);
 
@@ -350,12 +350,12 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if (likely(do_committed)) {
-                st = rrdset_find("mem.committed");
+                st = rrdset_find_localhost("mem.committed");
                 if (unlikely(!st)) {
-                    st = rrdset_create("mem", "committed", NULL, "system", NULL, "Committed (Allocated) Memory", "MB", 5000, update_every, RRDSET_TYPE_AREA);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("mem", "committed", NULL, "system", NULL, "Committed (Allocated) Memory", "MB", 5000, update_every, RRDSET_TYPE_AREA);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "Committed_AS", NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE);
+                    rrddim_add(st, "Committed_AS", NULL, system_pagesize, MEGA_FACTOR, RRD_ALGORITHM_ABSOLUTE);
                 }
                 else rrdset_next(st);
 
@@ -378,22 +378,22 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
                 error("DISABLED: system.cpu");
             } else {
 
-                st = rrdset_find_bytype("system", "cpu");
+                st = rrdset_find_bytype_localhost("system", "cpu");
                 if (unlikely(!st)) {
-                    st = rrdset_create("system", "cpu", NULL, "cpu", "system.cpu", "Total CPU utilization", "percentage", 100, update_every, RRDSET_TYPE_STACKED);
+                    st = rrdset_create_localhost("system", "cpu", NULL, "cpu", "system.cpu", "Total CPU utilization", "percentage", 100, update_every, RRDSET_TYPE_STACKED);
 
-                    rrddim_add(st, "user", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                    rrddim_add(st, "nice", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                    rrddim_add(st, "system", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                    rrddim_add(st, "interrupt", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                    rrddim_add(st, "idle", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "nice", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "system", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "user", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "interrupt", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "idle", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
                     rrddim_hide(st, "idle");
                 }
                 else rrdset_next(st);
 
-                rrddim_set(st, "user", cp_time[0]);
                 rrddim_set(st, "nice", cp_time[1]);
                 rrddim_set(st, "system", cp_time[2]);
+                rrddim_set(st, "user", cp_time[0]);
                 rrddim_set(st, "interrupt", cp_time[3]);
                 rrddim_set(st, "idle", cp_time[4]);
                 rrdset_done(st);
@@ -420,23 +420,23 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
                 } else {
                     for (i = 0; i < ncpus; i++) {
                         snprintfz(cpuid, MAX_INT_DIGITS, "cpu%d", i);
-                        st = rrdset_find_bytype("cpu", cpuid);
+                        st = rrdset_find_bytype_localhost("cpu", cpuid);
                         if (unlikely(!st)) {
-                            st = rrdset_create("cpu", cpuid, NULL, "utilization", "cpu.cpu", "Core utilization",
+                            st = rrdset_create_localhost("cpu", cpuid, NULL, "utilization", "cpu.cpu", "Core utilization",
                                                "percentage", 1000, update_every, RRDSET_TYPE_STACKED);
 
-                            rrddim_add(st, "user", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                            rrddim_add(st, "nice", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                            rrddim_add(st, "system", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                            rrddim_add(st, "interrupt", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                            rrddim_add(st, "idle", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+                            rrddim_add(st, "nice", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+                            rrddim_add(st, "system", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+                            rrddim_add(st, "user", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+                            rrddim_add(st, "interrupt", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+                            rrddim_add(st, "idle", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
                             rrddim_hide(st, "idle");
                         } else
                             rrdset_next(st);
 
-                        rrddim_set(st, "user", pcpu_cp_time[i * 5 + 0]);
                         rrddim_set(st, "nice", pcpu_cp_time[i * 5 + 1]);
                         rrddim_set(st, "system", pcpu_cp_time[i * 5 + 2]);
+                        rrddim_set(st, "user", pcpu_cp_time[i * 5 + 0]);
                         rrddim_set(st, "interrupt", pcpu_cp_time[i * 5 + 3]);
                         rrddim_set(st, "idle", pcpu_cp_time[i * 5 + 4]);
                         rrdset_done(st);
@@ -463,12 +463,12 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
                 for (i = 0; i < nintr; i++)
                     totalintr += intrcnt[i];
 
-                st = rrdset_find_bytype("system", "intr");
+                st = rrdset_find_bytype_localhost("system", "intr");
                 if (unlikely(!st)) {
-                    st = rrdset_create("system", "intr", NULL, "interrupts", NULL, "Total Hardware Interrupts", "interrupts/s", 900, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("system", "intr", NULL, "interrupts", NULL, "Total Hardware Interrupts", "interrupts/s", 900, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "interrupts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "interrupts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -483,9 +483,9 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
                     do_interrupts = 0;
                     error("DISABLED: system.intr");
                 } else {
-                    st = rrdset_find_bytype("system", "interrupts");
+                    st = rrdset_find_bytype_localhost("system", "interrupts");
                     if (unlikely(!st))
-                        st = rrdset_create("system", "interrupts", NULL, "interrupts", NULL, "System interrupts", "interrupts/s",
+                        st = rrdset_create_localhost("system", "interrupts", NULL, "interrupts", NULL, "System interrupts", "interrupts/s",
                                            1000, update_every, RRDSET_TYPE_STACKED);
                     else
                         rrdset_next(st);
@@ -495,7 +495,7 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
                         if (unlikely((intrcnt[i] != 0) && (*(char*)p != 0))) {
                             rd = rrddim_find(st, p);
                             if (unlikely(!rd))
-                                rd = rrddim_add(st, p, NULL, 1, 1, RRDDIM_INCREMENTAL);
+                                rd = rrddim_add(st, p, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
                             rrddim_set_by_pointer(st, rd, intrcnt[i]);
                         }
                     }
@@ -513,11 +513,11 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
             error("DISABLED: system.dev_intr");
         } else {
 
-            st = rrdset_find_bytype("system", "dev_intr");
+            st = rrdset_find_bytype_localhost("system", "dev_intr");
             if (unlikely(!st)) {
-                st = rrdset_create("system", "dev_intr", NULL, "interrupts", NULL, "Device Interrupts", "interrupts/s", 1000, update_every, RRDSET_TYPE_LINE);
+                st = rrdset_create_localhost("system", "dev_intr", NULL, "interrupts", NULL, "Device Interrupts", "interrupts/s", 1000, update_every, RRDSET_TYPE_LINE);
 
-                rrddim_add(st, "interrupts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "interrupts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(st);
 
@@ -534,11 +534,11 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
             error("DISABLED: system.dev_intr");
         } else {
 
-            st = rrdset_find_bytype("system", "soft_intr");
+            st = rrdset_find_bytype_localhost("system", "soft_intr");
             if (unlikely(!st)) {
-                st = rrdset_create("system", "soft_intr", NULL, "interrupts", NULL, "Software Interrupts", "interrupts/s", 1100, update_every, RRDSET_TYPE_LINE);
+                st = rrdset_create_localhost("system", "soft_intr", NULL, "interrupts", NULL, "Software Interrupts", "interrupts/s", 1100, update_every, RRDSET_TYPE_LINE);
 
-                rrddim_add(st, "interrupts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "interrupts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(st);
 
@@ -555,11 +555,11 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
             error("DISABLED: system.ctxt");
         } else {
 
-            st = rrdset_find_bytype("system", "ctxt");
+            st = rrdset_find_bytype_localhost("system", "ctxt");
             if (unlikely(!st)) {
-                st = rrdset_create("system", "ctxt", NULL, "processes", NULL, "CPU Context Switches", "context switches/s", 800, update_every, RRDSET_TYPE_LINE);
+                st = rrdset_create_localhost("system", "ctxt", NULL, "processes", NULL, "CPU Context Switches", "context switches/s", 800, update_every, RRDSET_TYPE_LINE);
 
-                rrddim_add(st, "switches", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "switches", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(st);
 
@@ -576,12 +576,12 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
             error("DISABLED: system.forks");
         } else {
 
-            st = rrdset_find_bytype("system", "forks");
+            st = rrdset_find_bytype_localhost("system", "forks");
             if (unlikely(!st)) {
-                st = rrdset_create("system", "forks", NULL, "processes", NULL, "Started Processes", "processes/s", 700, update_every, RRDSET_TYPE_LINE);
-                st->isdetail = 1;
+                st = rrdset_create_localhost("system", "forks", NULL, "processes", NULL, "Started Processes", "processes/s", 700, update_every, RRDSET_TYPE_LINE);
+                rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                rrddim_add(st, "started", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "started", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(st);
 
@@ -612,12 +612,12 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
                         // --------------------------------------------------------------------
 
-                        st = rrdset_find_bytype(RRD_TYPE_DISK, disk);
+                        st = rrdset_find_bytype_localhost(RRD_TYPE_DISK, disk);
                         if (unlikely(!st)) {
-                            st = rrdset_create(RRD_TYPE_DISK, disk, NULL, disk, "disk.io", "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every, RRDSET_TYPE_AREA);
+                            st = rrdset_create_localhost(RRD_TYPE_DISK, disk, NULL, disk, "disk.io", "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every, RRDSET_TYPE_AREA);
 
-                            rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_INCREMENTAL);
-                            rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_INCREMENTAL);
+                            rrddim_add(st, "reads", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
+                            rrddim_add(st, "writes", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL);
                         }
                         else rrdset_next(st);
 
@@ -629,13 +629,13 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
                         // --------------------------------------------------------------------
 
-                        st = rrdset_find_bytype("disk_ops", disk);
+                        st = rrdset_find_bytype_localhost("disk_ops", disk);
                         if (unlikely(!st)) {
-                            st = rrdset_create("disk_ops", disk, NULL, disk, "disk.ops", "Disk Completed I/O Operations", "operations/s", 2001, update_every, RRDSET_TYPE_LINE);
-                            st->isdetail = 1;
+                            st = rrdset_create_localhost("disk_ops", disk, NULL, disk, "disk.ops", "Disk Completed I/O Operations", "operations/s", 2001, update_every, RRDSET_TYPE_LINE);
+                            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                            rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                            rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                            rrddim_add(st, "reads", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                            rrddim_add(st, "writes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                         }
                         else rrdset_next(st);
 
@@ -645,12 +645,12 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
                         // --------------------------------------------------------------------
 
-                        st = rrdset_find_bytype("disk_qops", disk);
+                        st = rrdset_find_bytype_localhost("disk_qops", disk);
                         if (unlikely(!st)) {
-                            st = rrdset_create("disk_qops", disk, NULL, disk, "disk.qops", "Disk Current I/O Operations", "operations", 2002, update_every, RRDSET_TYPE_LINE);
-                            st->isdetail = 1;
+                            st = rrdset_create_localhost("disk_qops", disk, NULL, disk, "disk.qops", "Disk Current I/O Operations", "operations", 2002, update_every, RRDSET_TYPE_LINE);
+                            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                            rrddim_add(st, "operations", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                            rrddim_add(st, "operations", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
                         }
                         else rrdset_next(st);
 
@@ -659,12 +659,12 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
                         // --------------------------------------------------------------------
 
-                        st = rrdset_find_bytype("disk_util", disk);
+                        st = rrdset_find_bytype_localhost("disk_util", disk);
                         if (unlikely(!st)) {
-                            st = rrdset_create("disk_util", disk, NULL, disk, "disk.util", "Disk Utilization Time", "% of time working", 2004, update_every, RRDSET_TYPE_AREA);
-                            st->isdetail = 1;
+                            st = rrdset_create_localhost("disk_util", disk, NULL, disk, "disk.util", "Disk Utilization Time", "% of time working", 2004, update_every, RRDSET_TYPE_AREA);
+                            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                            rrddim_add(st, "utilization", NULL, 1, 10, RRDDIM_INCREMENTAL);
+                            rrddim_add(st, "utilization", NULL, 1, 10, RRD_ALGORITHM_INCREMENTAL);
                         }
                         else rrdset_next(st);
 
@@ -674,13 +674,13 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
                         // --------------------------------------------------------------------
 
-                        st = rrdset_find_bytype("disk_iotime", disk);
+                        st = rrdset_find_bytype_localhost("disk_iotime", disk);
                         if (unlikely(!st)) {
-                            st = rrdset_create("disk_iotime", disk, NULL, disk, "disk.iotime", "Disk Total I/O Time", "milliseconds/s", 2022, update_every, RRDSET_TYPE_LINE);
-                            st->isdetail = 1;
+                            st = rrdset_create_localhost("disk_iotime", disk, NULL, disk, "disk.iotime", "Disk Total I/O Time", "milliseconds/s", 2022, update_every, RRDSET_TYPE_LINE);
+                            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                            rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                            rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                            rrddim_add(st, "reads", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                            rrddim_add(st, "writes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                         }
                         else rrdset_next(st);
 
@@ -698,13 +698,13 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
                             // --------------------------------------------------------------------
 
-                            st = rrdset_find_bytype("disk_await", disk);
+                            st = rrdset_find_bytype_localhost("disk_await", disk);
                             if (unlikely(!st)) {
-                                st = rrdset_create("disk_await", disk, NULL, disk, "disk.await", "Average Completed I/O Operation Time", "ms per operation", 2005, update_every, RRDSET_TYPE_LINE);
-                                st->isdetail = 1;
+                                st = rrdset_create_localhost("disk_await", disk, NULL, disk, "disk.await", "Average Completed I/O Operation Time", "ms per operation", 2005, update_every, RRDSET_TYPE_LINE);
+                                rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                                rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_ABSOLUTE);
-                                rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_ABSOLUTE);
+                                rrddim_add(st, "reads", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+                                rrddim_add(st, "writes", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE);
                             }
                             else rrdset_next(st);
 
@@ -716,13 +716,13 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
                             // --------------------------------------------------------------------
 
-                            st = rrdset_find_bytype("disk_avgsz", disk);
+                            st = rrdset_find_bytype_localhost("disk_avgsz", disk);
                             if (unlikely(!st)) {
-                                st = rrdset_create("disk_avgsz", disk, NULL, disk, "disk.avgsz", "Average Completed I/O Operation Bandwidth", "kilobytes per operation", 2006, update_every, RRDSET_TYPE_AREA);
-                                st->isdetail = 1;
+                                st = rrdset_create_localhost("disk_avgsz", disk, NULL, disk, "disk.avgsz", "Average Completed I/O Operation Bandwidth", "kilobytes per operation", 2006, update_every, RRDSET_TYPE_AREA);
+                                rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                                rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-                                rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_ABSOLUTE);
+                                rrddim_add(st, "reads", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
+                                rrddim_add(st, "writes", NULL, -1, 1024, RRD_ALGORITHM_ABSOLUTE);
                             }
                             else rrdset_next(st);
 
@@ -734,12 +734,12 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
                             // --------------------------------------------------------------------
 
-                            st = rrdset_find_bytype("disk_svctm", disk);
+                            st = rrdset_find_bytype_localhost("disk_svctm", disk);
                             if (unlikely(!st)) {
-                                st = rrdset_create("disk_svctm", disk, NULL, disk, "disk.svctm", "Average Service Time", "ms per operation", 2007, update_every, RRDSET_TYPE_LINE);
-                                st->isdetail = 1;
+                                st = rrdset_create_localhost("disk_svctm", disk, NULL, disk, "disk.svctm", "Average Service Time", "ms per operation", 2007, update_every, RRDSET_TYPE_LINE);
+                                rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                                rrddim_add(st, "svctm", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                                rrddim_add(st, "svctm", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
                             }
                             else rrdset_next(st);
 
@@ -752,11 +752,11 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
                 // --------------------------------------------------------------------
 
-                st = rrdset_find_bytype("system", "io");
+                st = rrdset_find_bytype_localhost("system", "io");
                 if (unlikely(!st)) {
-                    st = rrdset_create("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150, update_every, RRDSET_TYPE_AREA);
-                    rrddim_add(st, "in",  NULL,  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "out", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    st = rrdset_create_localhost("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150, update_every, RRDSET_TYPE_AREA);
+                    rrddim_add(st, "in",  NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "out", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -798,13 +798,13 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
             }
 
             if (likely(do_swap)) {
-                st = rrdset_find("system.swap");
+                st = rrdset_find_localhost("system.swap");
                 if (unlikely(!st)) {
-                    st = rrdset_create("system", "swap", NULL, "swap", NULL, "System Swap", "MB", 201, update_every, RRDSET_TYPE_STACKED);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("system", "swap", NULL, "swap", NULL, "System Swap", "MB", 201, update_every, RRDSET_TYPE_STACKED);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "free",    NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE);
-                    rrddim_add(st, "used",    NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE);
+                    rrddim_add(st, "free",    NULL, system_pagesize, MEGA_FACTOR, RRD_ALGORITHM_ABSOLUTE);
+                    rrddim_add(st, "used",    NULL, system_pagesize, MEGA_FACTOR, RRD_ALGORITHM_ABSOLUTE);
                 }
                 else rrdset_next(st);
 
@@ -829,18 +829,18 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
             do_ram = 0;
             error("DISABLED: system.ram");
         } else {
-            st = rrdset_find("system.ram");
+            st = rrdset_find_localhost("system.ram");
             if (unlikely(!st)) {
-                st = rrdset_create("system", "ram", NULL, "ram", NULL, "System RAM", "MB", 200, update_every, RRDSET_TYPE_STACKED);
+                st = rrdset_create_localhost("system", "ram", NULL, "ram", NULL, "System RAM", "MB", 200, update_every, RRDSET_TYPE_STACKED);
 
-                rrddim_add(st, "active",    NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE);
-                rrddim_add(st, "inactive",  NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE);
-                rrddim_add(st, "wired",     NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE);
+                rrddim_add(st, "active",    NULL, system_pagesize, MEGA_FACTOR, RRD_ALGORITHM_ABSOLUTE);
+                rrddim_add(st, "inactive",  NULL, system_pagesize, MEGA_FACTOR, RRD_ALGORITHM_ABSOLUTE);
+                rrddim_add(st, "wired",     NULL, system_pagesize, MEGA_FACTOR, RRD_ALGORITHM_ABSOLUTE);
 #if __FreeBSD_version < 1200016
-                rrddim_add(st, "cache",     NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE);
+                rrddim_add(st, "cache",     NULL, system_pagesize, MEGA_FACTOR, RRD_ALGORITHM_ABSOLUTE);
 #endif
-                rrddim_add(st, "buffers",   NULL, 1, MEGA_FACTOR, RRDDIM_ABSOLUTE);
-                rrddim_add(st, "free",      NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE);
+                rrddim_add(st, "buffers",   NULL, 1, MEGA_FACTOR, RRD_ALGORITHM_ABSOLUTE);
+                rrddim_add(st, "free",      NULL, system_pagesize, MEGA_FACTOR, RRD_ALGORITHM_ABSOLUTE);
             }
             else rrdset_next(st);
 
@@ -863,12 +863,12 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
             do_swapio = 0;
             error("DISABLED: system.swapio");
         } else {
-            st = rrdset_find("system.swapio");
+            st = rrdset_find_localhost("system.swapio");
             if (unlikely(!st)) {
-                st = rrdset_create("system", "swapio", NULL, "swap", NULL, "Swap I/O", "kilobytes/s", 250, update_every, RRDSET_TYPE_AREA);
+                st = rrdset_create_localhost("system", "swapio", NULL, "swap", NULL, "Swap I/O", "kilobytes/s", 250, update_every, RRDSET_TYPE_AREA);
 
-                rrddim_add(st, "in",  NULL, system_pagesize, 1024, RRDDIM_INCREMENTAL);
-                rrddim_add(st, "out", NULL, -system_pagesize, 1024, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "in",  NULL, system_pagesize, 1024, RRD_ALGORITHM_INCREMENTAL);
+                rrddim_add(st, "out", NULL, -system_pagesize, 1024, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(st);
 
@@ -889,16 +889,16 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
             do_pgfaults = 0;
             error("DISABLED: mem.pgfaults");
         } else {
-            st = rrdset_find("mem.pgfaults");
+            st = rrdset_find_localhost("mem.pgfaults");
             if (unlikely(!st)) {
-                st = rrdset_create("mem", "pgfaults", NULL, "system", NULL, "Memory Page Faults", "page faults/s", 500, update_every, RRDSET_TYPE_LINE);
-                st->isdetail = 1;
-
-                rrddim_add(st, "memory", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                rrddim_add(st, "io_requiring", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                rrddim_add(st, "cow", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                rrddim_add(st, "cow_optimized", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                rrddim_add(st, "in_transit", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                st = rrdset_create_localhost("mem", "pgfaults", NULL, "system", NULL, "Memory Page Faults", "page faults/s", 500, update_every, RRDSET_TYPE_LINE);
+                rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                rrddim_add(st, "memory", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                rrddim_add(st, "io_requiring", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                rrddim_add(st, "cow", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                rrddim_add(st, "cow_optimized", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                rrddim_add(st, "in_transit", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(st);
 
@@ -934,10 +934,10 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
                 // --------------------------------------------------------------------
 
-                st = rrdset_find("system.ipc_semaphores");
+                st = rrdset_find_localhost("system.ipc_semaphores");
                 if (unlikely(!st)) {
-                    st = rrdset_create("system", "ipc_semaphores", NULL, "ipc semaphores", NULL, "IPC Semaphores", "semaphores", 1000, rrd_update_every, RRDSET_TYPE_AREA);
-                    rrddim_add(st, "semaphores", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                    st = rrdset_create_localhost("system", "ipc_semaphores", NULL, "ipc semaphores", NULL, "IPC Semaphores", "semaphores", 1000, localhost->rrd_update_every, RRDSET_TYPE_AREA);
+                    rrddim_add(st, "semaphores", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
                 }
                 else rrdset_next(st);
 
@@ -946,10 +946,10 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
                 // --------------------------------------------------------------------
 
-                st = rrdset_find("system.ipc_semaphore_arrays");
+                st = rrdset_find_localhost("system.ipc_semaphore_arrays");
                 if (unlikely(!st)) {
-                    st = rrdset_create("system", "ipc_semaphore_arrays", NULL, "ipc semaphores", NULL, "IPC Semaphore Arrays", "arrays", 1000, rrd_update_every, RRDSET_TYPE_AREA);
-                    rrddim_add(st, "arrays", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                    st = rrdset_create_localhost("system", "ipc_semaphore_arrays", NULL, "ipc semaphores", NULL, "IPC Semaphore Arrays", "arrays", 1000, localhost->rrd_update_every, RRDSET_TYPE_AREA);
+                    rrddim_add(st, "arrays", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
                 }
                 else rrdset_next(st);
 
@@ -982,10 +982,10 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
                 // --------------------------------------------------------------------
 
-                st = rrdset_find("system.ipc_shared_mem_segs");
+                st = rrdset_find_localhost("system.ipc_shared_mem_segs");
                 if (unlikely(!st)) {
-                    st = rrdset_create("system", "ipc_shared_mem_segs", NULL, "ipc shared memory", NULL, "IPC Shared Memory Segments", "segments", 1000, rrd_update_every, RRDSET_TYPE_AREA);
-                    rrddim_add(st, "segments", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                    st = rrdset_create_localhost("system", "ipc_shared_mem_segs", NULL, "ipc shared memory", NULL, "IPC Shared Memory Segments", "segments", 1000, localhost->rrd_update_every, RRDSET_TYPE_AREA);
+                    rrddim_add(st, "segments", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
                 }
                 else rrdset_next(st);
 
@@ -994,10 +994,10 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
                 // --------------------------------------------------------------------
 
-                st = rrdset_find("system.ipc_shared_mem_size");
+                st = rrdset_find_localhost("system.ipc_shared_mem_size");
                 if (unlikely(!st)) {
-                    st = rrdset_create("system", "ipc_shared_mem_size", NULL, "ipc shared memory", NULL, "IPC Shared Memory Segments Size", "kilobytes", 1000, rrd_update_every, RRDSET_TYPE_AREA);
-                    rrddim_add(st, "allocated", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+                    st = rrdset_create_localhost("system", "ipc_shared_mem_size", NULL, "ipc shared memory", NULL, "IPC Shared Memory Segments Size", "kilobytes", 1000, localhost->rrd_update_every, RRDSET_TYPE_AREA);
+                    rrddim_add(st, "allocated", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
                 }
                 else rrdset_next(st);
 
@@ -1034,10 +1034,10 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
                 // --------------------------------------------------------------------
 
-                st = rrdset_find("system.ipc_msq_queues");
+                st = rrdset_find_localhost("system.ipc_msq_queues");
                 if (unlikely(!st)) {
-                    st = rrdset_create("system", "ipc_msq_queues", NULL, "ipc message queues", NULL, "Number of IPC Message Queues", "queues", 990, rrd_update_every, RRDSET_TYPE_AREA);
-                    rrddim_add(st, "queues", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                    st = rrdset_create_localhost("system", "ipc_msq_queues", NULL, "ipc message queues", NULL, "Number of IPC Message Queues", "queues", 990, localhost->rrd_update_every, RRDSET_TYPE_AREA);
+                    rrddim_add(st, "queues", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
                 }
                 else rrdset_next(st);
 
@@ -1046,10 +1046,10 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
                 // --------------------------------------------------------------------
 
-                st = rrdset_find("system.ipc_msq_messages");
+                st = rrdset_find_localhost("system.ipc_msq_messages");
                 if (unlikely(!st)) {
-                    st = rrdset_create("system", "ipc_msq_messages", NULL, "ipc message queues", NULL, "Number of Messages in IPC Message Queues", "messages", 1000, rrd_update_every, RRDSET_TYPE_AREA);
-                    rrddim_add(st, "messages", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                    st = rrdset_create_localhost("system", "ipc_msq_messages", NULL, "ipc message queues", NULL, "Number of Messages in IPC Message Queues", "messages", 1000, localhost->rrd_update_every, RRDSET_TYPE_AREA);
+                    rrddim_add(st, "messages", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
                 }
                 else rrdset_next(st);
 
@@ -1058,11 +1058,11 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
                 // --------------------------------------------------------------------
 
-                st = rrdset_find("system.ipc_msq_size");
+                st = rrdset_find_localhost("system.ipc_msq_size");
                 if (unlikely(!st)) {
-                    st = rrdset_create("system", "ipc_msq_size", NULL, "ipc message queues", NULL, "Size of IPC Message Queues", "bytes", 1100, rrd_update_every, RRDSET_TYPE_LINE);
-                    rrddim_add(st, "allocated", NULL, 1, 1, RRDDIM_ABSOLUTE);
-                    rrddim_add(st, "used", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                    st = rrdset_create_localhost("system", "ipc_msq_size", NULL, "ipc message queues", NULL, "Size of IPC Message Queues", "bytes", 1100, localhost->rrd_update_every, RRDSET_TYPE_LINE);
+                    rrddim_add(st, "allocated", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+                    rrddim_add(st, "used", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
                 }
                 else rrdset_next(st);
 
@@ -1129,13 +1129,13 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if (likely(do_netisr)) {
-        st = rrdset_find_bytype("system", "softnet_stat");
+        st = rrdset_find_bytype_localhost("system", "softnet_stat");
         if (unlikely(!st)) {
-            st = rrdset_create("system", "softnet_stat", NULL, "softnet_stat", NULL, "System softnet_stat", "events/s", 955, update_every, RRDSET_TYPE_LINE);
-            rrddim_add(st, "dispatched", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "hybrid_dispatched", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "qdrops", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "queued", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            st = rrdset_create_localhost("system", "softnet_stat", NULL, "softnet_stat", NULL, "System softnet_stat", "events/s", 955, update_every, RRDSET_TYPE_LINE);
+            rrddim_add(st, "dispatched", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "hybrid_dispatched", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "qdrops", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "queued", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -1152,13 +1152,13 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
         for (i = 0; i < ncpus ;i++) {
             snprintfz(netstat_cpuid, 21, "cpu%d_softnet_stat", i);
 
-            st = rrdset_find_bytype("cpu", netstat_cpuid);
+            st = rrdset_find_bytype_localhost("cpu", netstat_cpuid);
             if (unlikely(!st)) {
-                st = rrdset_create("cpu", netstat_cpuid, NULL, "softnet_stat", NULL, "Per CPU netisr statistics", "events/s", 1101 + i, update_every, RRDSET_TYPE_LINE);
-                rrddim_add(st, "dispatched", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                rrddim_add(st, "hybrid_dispatched", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                rrddim_add(st, "qdrops", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                rrddim_add(st, "queued", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                st = rrdset_create_localhost("cpu", netstat_cpuid, NULL, "softnet_stat", NULL, "Per CPU netisr statistics", "events/s", 1101 + i, update_every, RRDSET_TYPE_LINE);
+                rrddim_add(st, "dispatched", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                rrddim_add(st, "hybrid_dispatched", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                rrddim_add(st, "qdrops", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                rrddim_add(st, "queued", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(st);
 
@@ -1186,12 +1186,12 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
                 iftot.ift_obytes += IFA_DATA(obytes);
             }
 
-            st = rrdset_find("system.ipv4");
+            st = rrdset_find_localhost("system.ipv4");
             if (unlikely(!st)) {
-                st = rrdset_create("system", "ipv4", NULL, "network", NULL, "IPv4 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA);
+                st = rrdset_create_localhost("system", "ipv4", NULL, "network", NULL, "IPv4 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA);
 
-                rrddim_add(st, "InOctets", "received", 8, 1024, RRDDIM_INCREMENTAL);
-                rrddim_add(st, "OutOctets", "sent", -8, 1024, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "InOctets", "received", 8, 1024, RRD_ALGORITHM_INCREMENTAL);
+                rrddim_add(st, "OutOctets", "sent", -8, 1024, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(st);
 
@@ -1209,12 +1209,12 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
                 iftot.ift_obytes += IFA_DATA(obytes);
             }
 
-            st = rrdset_find("system.ipv6");
+            st = rrdset_find_localhost("system.ipv6");
             if (unlikely(!st)) {
-                st = rrdset_create("system", "ipv6", NULL, "network", NULL, "IPv6 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA);
+                st = rrdset_create_localhost("system", "ipv6", NULL, "network", NULL, "IPv6 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA);
 
-                rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
-                rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "received", NULL, 8, 1024, RRD_ALGORITHM_INCREMENTAL);
+                rrddim_add(st, "sent", NULL, -8, 1024, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(st);
 
@@ -1228,12 +1228,12 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
                 // --------------------------------------------------------------------
 
-                st = rrdset_find_bytype("net", ifa->ifa_name);
+                st = rrdset_find_bytype_localhost("net", ifa->ifa_name);
                 if (unlikely(!st)) {
-                    st = rrdset_create("net", ifa->ifa_name, NULL, ifa->ifa_name, "net.net", "Bandwidth", "kilobits/s", 7000, update_every, RRDSET_TYPE_AREA);
+                    st = rrdset_create_localhost("net", ifa->ifa_name, NULL, ifa->ifa_name, "net.net", "Bandwidth", "kilobits/s", 7000, update_every, RRDSET_TYPE_AREA);
 
-                    rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "received", NULL, 8, 1024, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "sent", NULL, -8, 1024, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -1243,15 +1243,15 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
                 // --------------------------------------------------------------------
 
-                st = rrdset_find_bytype("net_packets", ifa->ifa_name);
+                st = rrdset_find_bytype_localhost("net_packets", ifa->ifa_name);
                 if (unlikely(!st)) {
-                    st = rrdset_create("net_packets", ifa->ifa_name, NULL, ifa->ifa_name, "net.packets", "Packets", "packets/s", 7001, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("net_packets", ifa->ifa_name, NULL, ifa->ifa_name, "net.packets", "Packets", "packets/s", 7001, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "multicast_received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "multicast_sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "multicast_received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "multicast_sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -1263,13 +1263,13 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
                 // --------------------------------------------------------------------
 
-                st = rrdset_find_bytype("net_errors", ifa->ifa_name);
+                st = rrdset_find_bytype_localhost("net_errors", ifa->ifa_name);
                 if (unlikely(!st)) {
-                    st = rrdset_create("net_errors", ifa->ifa_name, NULL, ifa->ifa_name, "net.errors", "Interface Errors", "errors/s", 7002, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("net_errors", ifa->ifa_name, NULL, ifa->ifa_name, "net.errors", "Interface Errors", "errors/s", 7002, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "outbound", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -1279,14 +1279,14 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
                 // --------------------------------------------------------------------
 
-                st = rrdset_find_bytype("net_drops", ifa->ifa_name);
+                st = rrdset_find_bytype_localhost("net_drops", ifa->ifa_name);
                 if (unlikely(!st)) {
-                    st = rrdset_create("net_drops", ifa->ifa_name, NULL, ifa->ifa_name, "net.drops", "Interface Drops", "drops/s", 7003, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("net_drops", ifa->ifa_name, NULL, ifa->ifa_name, "net.drops", "Interface Drops", "drops/s", 7003, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
 #if __FreeBSD__ >= 11
-                    rrddim_add(st, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "outbound", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
 #endif
                 }
                 else rrdset_next(st);
@@ -1299,14 +1299,14 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
                 // --------------------------------------------------------------------
 
-                st = rrdset_find_bytype("net_events", ifa->ifa_name);
+                st = rrdset_find_bytype_localhost("net_events", ifa->ifa_name);
                 if (unlikely(!st)) {
-                    st = rrdset_create("net_events", ifa->ifa_name, NULL, ifa->ifa_name, "net.events", "Network Interface Events", "events/s", 7006, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("net_events", ifa->ifa_name, NULL, ifa->ifa_name, "net.events", "Network Interface Events", "events/s", 7006, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "frames", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "collisions", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "carrier", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "frames", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "collisions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "carrier", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -1327,12 +1327,12 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
             error("DISABLED: ipv4.tcpsock");
         } else {
             if (likely(do_tcp_sockets)) {
-                st = rrdset_find("ipv4.tcpsock");
+                st = rrdset_find_localhost("ipv4.tcpsock");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "tcpsock", NULL, "tcp", NULL, "IPv4 TCP Connections",
+                    st = rrdset_create_localhost("ipv4", "tcpsock", NULL, "tcp", NULL, "IPv4 TCP Connections",
                                        "active connections", 2500, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "CurrEstab", "connections", 1, 1, RRDDIM_ABSOLUTE);
+                    rrddim_add(st, "CurrEstab", "connections", 1, 1, RRD_ALGORITHM_ABSOLUTE);
                 } else
                     rrdset_next(st);
 
@@ -1363,14 +1363,14 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
             error("DISABLED: ipv4.ecnpkts");
         } else {
             if (likely(do_tcp_packets)) {
-                st = rrdset_find("ipv4.tcppackets");
+                st = rrdset_find_localhost("ipv4.tcppackets");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "tcppackets", NULL, "tcp", NULL, "IPv4 TCP Packets",
+                    st = rrdset_create_localhost("ipv4", "tcppackets", NULL, "tcp", NULL, "IPv4 TCP Packets",
                                        "packets/s",
                                        2600, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InSegs", "received", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutSegs", "sent", -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InSegs", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutSegs", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -1382,16 +1382,16 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if (likely(do_tcp_errors)) {
-                st = rrdset_find("ipv4.tcperrors");
+                st = rrdset_find_localhost("ipv4.tcperrors");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "tcperrors", NULL, "tcp", NULL, "IPv4 TCP Errors",
+                    st = rrdset_create_localhost("ipv4", "tcperrors", NULL, "tcp", NULL, "IPv4 TCP Errors",
                                        "packets/s",
                                        2700, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "InErrs", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "RetransSegs", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InErrs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "RetransSegs", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -1408,17 +1408,17 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if (likely(do_tcp_handshake)) {
-                st = rrdset_find("ipv4.tcphandshake");
+                st = rrdset_find_localhost("ipv4.tcphandshake");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "tcphandshake", NULL, "tcp", NULL,
+                    st = rrdset_create_localhost("ipv4", "tcphandshake", NULL, "tcp", NULL,
                                        "IPv4 TCP Handshake Issues",
                                        "events/s", 2900, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "EstabResets", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "ActiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "PassiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "AttemptFails", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "EstabResets", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "ActiveOpens", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "PassiveOpens", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "AttemptFails", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -1431,17 +1431,17 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_tcpext_connaborts == CONFIG_ONDEMAND_YES || (do_tcpext_connaborts == CONFIG_ONDEMAND_ONDEMAND && (tcpstat.tcps_rcvpackafterwin || tcpstat.tcps_rcvafterclose || tcpstat.tcps_rcvmemdrop || tcpstat.tcps_persistdrop || tcpstat.tcps_finwait2_drops))) {
-                do_tcpext_connaborts = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv4.tcpconnaborts");
+            if (do_tcpext_connaborts == CONFIG_BOOLEAN_YES || (do_tcpext_connaborts == CONFIG_BOOLEAN_AUTO && (tcpstat.tcps_rcvpackafterwin || tcpstat.tcps_rcvafterclose || tcpstat.tcps_rcvmemdrop || tcpstat.tcps_persistdrop || tcpstat.tcps_finwait2_drops))) {
+                do_tcpext_connaborts = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv4.tcpconnaborts");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "tcpconnaborts", NULL, "tcp", NULL, "TCP Connection Aborts", "connections/s", 3010, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv4", "tcpconnaborts", NULL, "tcp", NULL, "TCP Connection Aborts", "connections/s", 3010, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "TCPAbortOnData",    "baddata",     1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "TCPAbortOnClose",   "userclosed",  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "TCPAbortOnMemory",  "nomemory",    1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "TCPAbortOnTimeout", "timeout",     1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "TCPAbortOnLinger",  "linger",      1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "TCPAbortOnData",    "baddata",     1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "TCPAbortOnClose",   "userclosed",  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "TCPAbortOnMemory",  "nomemory",    1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "TCPAbortOnTimeout", "timeout",     1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "TCPAbortOnLinger",  "linger",      1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -1455,13 +1455,13 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_tcpext_ofo == CONFIG_ONDEMAND_YES || (do_tcpext_ofo == CONFIG_ONDEMAND_ONDEMAND && tcpstat.tcps_rcvoopack)) {
-                do_tcpext_ofo = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv4.tcpofo");
+            if (do_tcpext_ofo == CONFIG_BOOLEAN_YES || (do_tcpext_ofo == CONFIG_BOOLEAN_AUTO && tcpstat.tcps_rcvoopack)) {
+                do_tcpext_ofo = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv4.tcpofo");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "tcpofo", NULL, "tcp", NULL, "TCP Out-Of-Order Queue", "packets/s", 3050, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv4", "tcpofo", NULL, "tcp", NULL, "TCP Out-Of-Order Queue", "packets/s", 3050, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "TCPOFOQueue", "inqueue",  1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "TCPOFOQueue", "inqueue",  1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -1471,16 +1471,16 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_tcpext_syscookies == CONFIG_ONDEMAND_YES || (do_tcpext_syscookies == CONFIG_ONDEMAND_ONDEMAND && (tcpstat.tcps_sc_sendcookie || tcpstat.tcps_sc_recvcookie || tcpstat.tcps_sc_zonefail))) {
-                do_tcpext_syscookies = CONFIG_ONDEMAND_YES;
+            if (do_tcpext_syscookies == CONFIG_BOOLEAN_YES || (do_tcpext_syscookies == CONFIG_BOOLEAN_AUTO && (tcpstat.tcps_sc_sendcookie || tcpstat.tcps_sc_recvcookie || tcpstat.tcps_sc_zonefail))) {
+                do_tcpext_syscookies = CONFIG_BOOLEAN_YES;
 
-                st = rrdset_find("ipv4.tcpsyncookies");
+                st = rrdset_find_localhost("ipv4.tcpsyncookies");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "tcpsyncookies", NULL, "tcp", NULL, "TCP SYN Cookies", "packets/s", 3100, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv4", "tcpsyncookies", NULL, "tcp", NULL, "TCP SYN Cookies", "packets/s", 3100, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "SyncookiesRecv",   "received",  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "SyncookiesSent",   "sent",     -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "SyncookiesFailed", "failed",   -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "SyncookiesRecv",   "received",  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "SyncookiesSent",   "sent",     -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "SyncookiesFailed", "failed",   -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -1492,17 +1492,17 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_ecn == CONFIG_ONDEMAND_YES || (do_ecn == CONFIG_ONDEMAND_ONDEMAND && (tcpstat.tcps_ecn_ce || tcpstat.tcps_ecn_ect0 || tcpstat.tcps_ecn_ect1))) {
-                do_ecn = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv4.ecnpkts");
+            if (do_ecn == CONFIG_BOOLEAN_YES || (do_ecn == CONFIG_BOOLEAN_AUTO && (tcpstat.tcps_ecn_ce || tcpstat.tcps_ecn_ect0 || tcpstat.tcps_ecn_ect1))) {
+                do_ecn = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv4.ecnpkts");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "ecnpkts", NULL, "ecn", NULL, "IPv4 ECN Statistics", "packets/s", 8700, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("ipv4", "ecnpkts", NULL, "ecn", NULL, "IPv4 ECN Statistics", "packets/s", 8700, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "InCEPkts", "CEP", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InNoECTPkts", "NoECTP", -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InECT0Pkts", "ECTP0", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InECT1Pkts", "ECTP1", 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InCEPkts", "CEP", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InNoECTPkts", "NoECTP", -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InECT0Pkts", "ECTP0", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InECT1Pkts", "ECTP1", 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -1527,13 +1527,13 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
             error("DISABLED: ipv4.udperrors");
         } else {
             if (likely(do_udp_packets)) {
-                st = rrdset_find("ipv4.udppackets");
+                st = rrdset_find_localhost("ipv4.udppackets");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "udppackets", NULL, "udp", NULL, "IPv4 UDP Packets",
+                    st = rrdset_create_localhost("ipv4", "udppackets", NULL, "udp", NULL, "IPv4 UDP Packets",
                                        "packets/s", 2601, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InDatagrams", "received", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InDatagrams", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -1545,17 +1545,17 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if (likely(do_udp_errors)) {
-                st = rrdset_find("ipv4.udperrors");
+                st = rrdset_find_localhost("ipv4.udperrors");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "udperrors", NULL, "udp", NULL, "IPv4 UDP Errors", "events/s",
+                    st = rrdset_create_localhost("ipv4", "udperrors", NULL, "udp", NULL, "IPv4 UDP Errors", "events/s",
                                        2701, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "NoPorts", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "NoPorts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -1588,14 +1588,14 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if (likely(do_icmp_packets)) {
-                st = rrdset_find("ipv4.icmp");
+                st = rrdset_find_localhost("ipv4.icmp");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "icmp", NULL, "icmp", NULL, "IPv4 ICMP Packets", "packets/s",
+                    st = rrdset_create_localhost("ipv4", "icmp", NULL, "icmp", NULL, "IPv4 ICMP Packets", "packets/s",
                                        2602,
                                        update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InMsgs", "received", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutMsgs", "sent", -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InMsgs", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutMsgs", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -1606,15 +1606,15 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
                 // --------------------------------------------------------------------
 
-                st = rrdset_find("ipv4.icmp_errors");
+                st = rrdset_find_localhost("ipv4.icmp_errors");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "icmp_errors", NULL, "icmp", NULL, "IPv4 ICMP Errors",
+                    st = rrdset_create_localhost("ipv4", "icmp_errors", NULL, "icmp", NULL, "IPv4 ICMP Errors",
                                        "packets/s",
                                        2603, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -1628,15 +1628,15 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if (likely(do_icmpmsg)) {
-                st = rrdset_find("ipv4.icmpmsg");
+                st = rrdset_find_localhost("ipv4.icmpmsg");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "icmpmsg", NULL, "icmp", NULL, "IPv4 ICMP Messsages",
+                    st = rrdset_create_localhost("ipv4", "icmpmsg", NULL, "icmp", NULL, "IPv4 ICMP Messsages",
                                        "packets/s", 2604, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InEchoReps", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutEchoReps", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InEchos", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutEchos", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InEchoReps", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutEchoReps", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InEchos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutEchos", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -1665,15 +1665,15 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
             error("DISABLED: ipv4.errors");
         } else {
             if (likely(do_ip_packets)) {
-                st = rrdset_find("ipv4.packets");
+                st = rrdset_find_localhost("ipv4.packets");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "packets", NULL, "packets", NULL, "IPv4 Packets", "packets/s",
+                    st = rrdset_create_localhost("ipv4", "packets", NULL, "packets", NULL, "IPv4 Packets", "packets/s",
                                        3000, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InReceives", "received", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutRequests", "sent", -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "ForwDatagrams", "forwarded", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InDelivers", "delivered", 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InReceives", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutRequests", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "ForwDatagrams", "forwarded", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InDelivers", "delivered", 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -1687,15 +1687,15 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if (likely(do_ip_fragsout)) {
-                st = rrdset_find("ipv4.fragsout");
+                st = rrdset_find_localhost("ipv4.fragsout");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "fragsout", NULL, "fragments", NULL, "IPv4 Fragments Sent",
+                    st = rrdset_create_localhost("ipv4", "fragsout", NULL, "fragments", NULL, "IPv4 Fragments Sent",
                                        "packets/s", 3010, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "FragOKs", "ok", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "FragFails", "failed", -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "FragCreates", "created", 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "FragOKs", "ok", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "FragFails", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "FragCreates", "created", 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -1708,16 +1708,16 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if (likely(do_ip_fragsin)) {
-                st = rrdset_find("ipv4.fragsin");
+                st = rrdset_find_localhost("ipv4.fragsin");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "fragsin", NULL, "fragments", NULL,
+                    st = rrdset_create_localhost("ipv4", "fragsin", NULL, "fragments", NULL,
                                        "IPv4 Fragments Reassembly",
                                        "packets/s", 3011, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "ReasmOKs", "ok", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "ReasmFails", "failed", -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "ReasmReqds", "all", 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "ReasmOKs", "ok", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "ReasmFails", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "ReasmReqds", "all", 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -1730,21 +1730,21 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if (likely(do_ip_errors)) {
-                st = rrdset_find("ipv4.errors");
+                st = rrdset_find_localhost("ipv4.errors");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "errors", NULL, "errors", NULL, "IPv4 Errors", "packets/s",
+                    st = rrdset_create_localhost("ipv4", "errors", NULL, "errors", NULL, "IPv4 Errors", "packets/s",
                                        3002,
                                        update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InDiscards", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutDiscards", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
 
-                    rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
 
-                    rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -1772,19 +1772,19 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
             do_ip6_errors = 0;
             error("DISABLED: ipv6.errors");
         } else {
-            if (do_ip6_packets == CONFIG_ONDEMAND_YES || (do_ip6_packets == CONFIG_ONDEMAND_ONDEMAND &&
+            if (do_ip6_packets == CONFIG_BOOLEAN_YES || (do_ip6_packets == CONFIG_BOOLEAN_AUTO &&
                                                           (ip6stat.ip6s_localout || ip6stat.ip6s_total ||
                                                            ip6stat.ip6s_forward || ip6stat.ip6s_delivered))) {
-                do_ip6_packets = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv6.packets");
+                do_ip6_packets = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv6.packets");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv6", "packets", NULL, "packets", NULL, "IPv6 Packets", "packets/s", 3000,
+                    st = rrdset_create_localhost("ipv6", "packets", NULL, "packets", NULL, "IPv6 Packets", "packets/s", 3000,
                                        update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "forwarded", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "delivers", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "forwarded", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "delivers", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -1797,19 +1797,19 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_ip6_fragsout == CONFIG_ONDEMAND_YES || (do_ip6_fragsout == CONFIG_ONDEMAND_ONDEMAND &&
+            if (do_ip6_fragsout == CONFIG_BOOLEAN_YES || (do_ip6_fragsout == CONFIG_BOOLEAN_AUTO &&
                                                            (ip6stat.ip6s_fragmented || ip6stat.ip6s_cantfrag ||
                                                             ip6stat.ip6s_ofragments))) {
-                do_ip6_fragsout = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv6.fragsout");
+                do_ip6_fragsout = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv6.fragsout");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv6", "fragsout", NULL, "fragments", NULL, "IPv6 Fragments Sent",
+                    st = rrdset_create_localhost("ipv6", "fragsout", NULL, "fragments", NULL, "IPv6 Fragments Sent",
                                        "packets/s", 3010, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "ok", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "failed", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "all", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -1821,20 +1821,20 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_ip6_fragsin == CONFIG_ONDEMAND_YES || (do_ip6_fragsin == CONFIG_ONDEMAND_ONDEMAND &&
+            if (do_ip6_fragsin == CONFIG_BOOLEAN_YES || (do_ip6_fragsin == CONFIG_BOOLEAN_AUTO &&
                                                           (ip6stat.ip6s_reassembled || ip6stat.ip6s_fragdropped ||
                                                            ip6stat.ip6s_fragtimeout || ip6stat.ip6s_fragments))) {
-                do_ip6_fragsin = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv6.fragsin");
+                do_ip6_fragsin = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv6.fragsin");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv6", "fragsin", NULL, "fragments", NULL, "IPv6 Fragments Reassembly",
+                    st = rrdset_create_localhost("ipv6", "fragsin", NULL, "fragments", NULL, "IPv6 Fragments Reassembly",
                                        "packets/s", 3011, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "timeout", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "ok", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "failed", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "timeout", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "all", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -1847,7 +1847,7 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_ip6_errors == CONFIG_ONDEMAND_YES || (do_ip6_errors == CONFIG_ONDEMAND_ONDEMAND && (
+            if (do_ip6_errors == CONFIG_BOOLEAN_YES || (do_ip6_errors == CONFIG_BOOLEAN_AUTO && (
                     ip6stat.ip6s_toosmall ||
                     ip6stat.ip6s_odropped ||
                     ip6stat.ip6s_badoptions ||
@@ -1857,22 +1857,22 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
                     ip6stat.ip6s_tooshort ||
                     ip6stat.ip6s_cantforward ||
                     ip6stat.ip6s_noroute))) {
-                do_ip6_errors = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv6.errors");
+                do_ip6_errors = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv6.errors");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv6", "errors", NULL, "errors", NULL, "IPv6 Errors", "packets/s", 3002,
+                    st = rrdset_create_localhost("ipv6", "errors", NULL, "errors", NULL, "IPv6 Errors", "packets/s", 3002,
                                        update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InDiscards", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutDiscards", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
 
-                    rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InTruncatedPkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InNoRoutes", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InTruncatedPkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InNoRoutes", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
 
-                    rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -1903,15 +1903,15 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
                 icmp6_total.msgs_out += icmp6stat.icp6s_outhist[i];
             }
             icmp6_total.msgs_in += icmp6stat.icp6s_badcode + icmp6stat.icp6s_badlen + icmp6stat.icp6s_checksum + icmp6stat.icp6s_tooshort;
-            if (do_icmp6 == CONFIG_ONDEMAND_YES || (do_icmp6 == CONFIG_ONDEMAND_ONDEMAND && (icmp6_total.msgs_in || icmp6_total.msgs_out))) {
-                do_icmp6 = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv6.icmp");
+            if (do_icmp6 == CONFIG_BOOLEAN_YES || (do_icmp6 == CONFIG_BOOLEAN_AUTO && (icmp6_total.msgs_in || icmp6_total.msgs_out))) {
+                do_icmp6 = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv6.icmp");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv6", "icmp", NULL, "icmp", NULL, "IPv6 ICMP Messages",
+                    st = rrdset_create_localhost("ipv6", "icmp", NULL, "icmp", NULL, "IPv6 ICMP Messages",
                                        "messages/s", 10000, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -1922,15 +1922,15 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_icmp6_redir == CONFIG_ONDEMAND_YES || (do_icmp6_redir == CONFIG_ONDEMAND_ONDEMAND && (icmp6stat.icp6s_inhist[ND_REDIRECT] || icmp6stat.icp6s_outhist[ND_REDIRECT]))) {
-                do_icmp6_redir = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv6.icmpredir");
+            if (do_icmp6_redir == CONFIG_BOOLEAN_YES || (do_icmp6_redir == CONFIG_BOOLEAN_AUTO && (icmp6stat.icp6s_inhist[ND_REDIRECT] || icmp6stat.icp6s_outhist[ND_REDIRECT]))) {
+                do_icmp6_redir = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv6.icmpredir");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv6", "icmpredir", NULL, "icmp", NULL, "IPv6 ICMP Redirects",
+                    st = rrdset_create_localhost("ipv6", "icmpredir", NULL, "icmp", NULL, "IPv6 ICMP Redirects",
                                        "redirects/s", 10050, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -1941,7 +1941,7 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_icmp6_errors == CONFIG_ONDEMAND_YES || (do_icmp6_errors == CONFIG_ONDEMAND_ONDEMAND && (
+            if (do_icmp6_errors == CONFIG_BOOLEAN_YES || (do_icmp6_errors == CONFIG_BOOLEAN_AUTO && (
                                                                             icmp6stat.icp6s_badcode ||
                                                                             icmp6stat.icp6s_badlen ||
                                                                             icmp6stat.icp6s_checksum ||
@@ -1953,22 +1953,22 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
                                                                             icmp6stat.icp6s_outhist[ICMP6_DST_UNREACH] ||
                                                                             icmp6stat.icp6s_outhist[ICMP6_TIME_EXCEEDED] ||
                                                                             icmp6stat.icp6s_outhist[ICMP6_PARAM_PROB]))) {
-                do_icmp6_errors = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv6.icmperrors");
+                do_icmp6_errors = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv6.icmperrors");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv6", "icmperrors", NULL, "icmp", NULL, "IPv6 ICMP Errors", "errors/s", 10100, update_every, RRDSET_TYPE_LINE);
-
-                    rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
-
-                    rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InDestUnreachs", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InPktTooBigs", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InTimeExcds", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InParmProblems", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutDestUnreachs", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutTimeExcds", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutParmProblems", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    st = rrdset_create_localhost("ipv6", "icmperrors", NULL, "icmp", NULL, "IPv6 ICMP Errors", "errors/s", 10100, update_every, RRDSET_TYPE_LINE);
+
+                    rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+
+                    rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InDestUnreachs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InPktTooBigs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InTimeExcds", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InParmProblems", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutDestUnreachs", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutTimeExcds", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutParmProblems", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -1987,20 +1987,20 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_icmp6_echos == CONFIG_ONDEMAND_YES || (do_icmp6_echos == CONFIG_ONDEMAND_ONDEMAND && (
+            if (do_icmp6_echos == CONFIG_BOOLEAN_YES || (do_icmp6_echos == CONFIG_BOOLEAN_AUTO && (
                                                                  icmp6stat.icp6s_inhist[ICMP6_ECHO_REQUEST] ||
                                                                  icmp6stat.icp6s_outhist[ICMP6_ECHO_REQUEST] ||
                                                                  icmp6stat.icp6s_inhist[ICMP6_ECHO_REPLY] ||
                                                                  icmp6stat.icp6s_outhist[ICMP6_ECHO_REPLY]))) {
-                do_icmp6_echos = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv6.icmpechos");
+                do_icmp6_echos = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv6.icmpechos");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv6", "icmpechos", NULL, "icmp", NULL, "IPv6 ICMP Echo", "messages/s", 10200, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv6", "icmpechos", NULL, "icmp", NULL, "IPv6 ICMP Echo", "messages/s", 10200, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InEchos", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutEchos", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InEchoReplies", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutEchoReplies", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InEchos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutEchos", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InEchoReplies", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutEchoReplies", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -2013,20 +2013,20 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_icmp6_router == CONFIG_ONDEMAND_YES || (do_icmp6_router == CONFIG_ONDEMAND_ONDEMAND && (
+            if (do_icmp6_router == CONFIG_BOOLEAN_YES || (do_icmp6_router == CONFIG_BOOLEAN_AUTO && (
                                                                     icmp6stat.icp6s_inhist[ND_ROUTER_SOLICIT] ||
                                                                     icmp6stat.icp6s_outhist[ND_ROUTER_SOLICIT] ||
                                                                     icmp6stat.icp6s_inhist[ND_ROUTER_ADVERT] ||
                                                                     icmp6stat.icp6s_outhist[ND_ROUTER_ADVERT]))) {
-                do_icmp6_router = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv6.icmprouter");
+                do_icmp6_router = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv6.icmprouter");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv6", "icmprouter", NULL, "icmp", NULL, "IPv6 Router Messages", "messages/s", 10400, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv6", "icmprouter", NULL, "icmp", NULL, "IPv6 Router Messages", "messages/s", 10400, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InSolicits", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutSolicits", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InSolicits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutSolicits", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -2039,20 +2039,20 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_icmp6_neighbor == CONFIG_ONDEMAND_YES || (do_icmp6_neighbor == CONFIG_ONDEMAND_ONDEMAND && (
+            if (do_icmp6_neighbor == CONFIG_BOOLEAN_YES || (do_icmp6_neighbor == CONFIG_BOOLEAN_AUTO && (
                                                                     icmp6stat.icp6s_inhist[ND_NEIGHBOR_SOLICIT] ||
                                                                     icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT] ||
                                                                     icmp6stat.icp6s_inhist[ND_NEIGHBOR_ADVERT] ||
                                                                     icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]))) {
-                do_icmp6_neighbor = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv6.icmpneighbor");
+                do_icmp6_neighbor = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv6.icmpneighbor");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv6", "icmpneighbor", NULL, "icmp", NULL, "IPv6 Neighbor Messages", "messages/s", 10500, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv6", "icmpneighbor", NULL, "icmp", NULL, "IPv6 Neighbor Messages", "messages/s", 10500, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InSolicits", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutSolicits", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InSolicits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutSolicits", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -2065,7 +2065,7 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_icmp6_types == CONFIG_ONDEMAND_YES || (do_icmp6_types == CONFIG_ONDEMAND_ONDEMAND && (
+            if (do_icmp6_types == CONFIG_BOOLEAN_YES || (do_icmp6_types == CONFIG_BOOLEAN_AUTO && (
                                                                     icmp6stat.icp6s_inhist[1] ||
                                                                     icmp6stat.icp6s_inhist[128] ||
                                                                     icmp6stat.icp6s_inhist[129] ||
@@ -2076,22 +2076,22 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
                                                                     icmp6stat.icp6s_outhist[133] ||
                                                                     icmp6stat.icp6s_outhist[135] ||
                                                                     icmp6stat.icp6s_outhist[136]))) {
-                do_icmp6_types = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv6.icmptypes");
+                do_icmp6_types = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv6.icmptypes");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv6", "icmptypes", NULL, "icmp", NULL, "IPv6 ICMP Types",
+                    st = rrdset_create_localhost("ipv6", "icmptypes", NULL, "icmp", NULL, "IPv6 ICMP Types",
                                        "messages/s", 10700, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InType1", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InType128", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InType129", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InType136", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutType1", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutType128", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutType129", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutType133", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutType135", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutType143", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InType1", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InType128", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InType129", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InType136", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutType1", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutType128", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutType129", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutType133", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutType135", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutType143", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -2135,17 +2135,17 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
                 // --------------------------------------------------------------------------
 
                 if (likely(do_space)) {
-                    st = rrdset_find_bytype("disk_space", mntbuf[i].f_mntonname);
+                    st = rrdset_find_bytype_localhost("disk_space", mntbuf[i].f_mntonname);
                     if (unlikely(!st)) {
                         snprintfz(title, 4096, "Disk Space Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname);
-                        st = rrdset_create("disk_space", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname, "disk.space", title, "GB", 2023,
+                        st = rrdset_create_localhost("disk_space", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname, "disk.space", title, "GB", 2023,
                                            update_every,
                                            RRDSET_TYPE_STACKED);
 
-                        rrddim_add(st, "avail", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRDDIM_ABSOLUTE);
-                        rrddim_add(st, "used", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRDDIM_ABSOLUTE);
+                        rrddim_add(st, "avail", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE);
+                        rrddim_add(st, "used", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE);
                         rrddim_add(st, "reserved_for_root", "reserved for root", mntbuf[i].f_bsize, GIGA_FACTOR,
-                                   RRDDIM_ABSOLUTE);
+                                RRD_ALGORITHM_ABSOLUTE);
                     } else
                         rrdset_next(st);
 
@@ -2158,15 +2158,15 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
                 // --------------------------------------------------------------------------
 
                 if (likely(do_inodes)) {
-                    st = rrdset_find_bytype("disk_inodes", mntbuf[i].f_mntonname);
+                    st = rrdset_find_bytype_localhost("disk_inodes", mntbuf[i].f_mntonname);
                     if (unlikely(!st)) {
                         snprintfz(title, 4096, "Disk Files (inodes) Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname);
-                        st = rrdset_create("disk_inodes", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname, "disk.inodes", title, "Inodes", 2024,
+                        st = rrdset_create_localhost("disk_inodes", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname, "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);
+                        rrddim_add(st, "avail", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+                        rrddim_add(st, "used", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+                        rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1, RRD_ALGORITHM_ABSOLUTE);
                     } else
                         rrdset_next(st);
 
@@ -2186,11 +2186,11 @@ int do_freebsd_sysctl(int update_every, usec_t dt) {
             error("DISABLED: system.uptime");
         } else {
             clock_gettime(CLOCK_REALTIME, &cur_time);
-            st = rrdset_find("system.uptime");
+            st = rrdset_find_localhost("system.uptime");
 
             if(unlikely(!st)) {
-                st = rrdset_create("system", "uptime", NULL, "uptime", NULL, "System Uptime", "seconds", 1000, update_every, RRDSET_TYPE_LINE);
-                rrddim_add(st, "uptime", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                st = rrdset_create_localhost("system", "uptime", NULL, "uptime", NULL, "System Uptime", "seconds", 1000, update_every, RRDSET_TYPE_LINE);
+                rrddim_add(st, "uptime", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
             }
             else rrdset_next(st);
 
index a698615f4e770aaf9e947b1b7419f44e8a42872a..c3d63043e13967a6efbb48fcfb4734d271c3f078 100644 (file)
@@ -129,14 +129,14 @@ void global_statistics_charts(void) {
     getrusage(RUSAGE_THREAD, &thread);
     getrusage(RUSAGE_SELF, &me);
 
-    if (!stcpu_thread) stcpu_thread = rrdset_find("netdata.plugin_proc_cpu");
+    if (!stcpu_thread) stcpu_thread = rrdset_find_localhost("netdata.plugin_proc_cpu");
     if (!stcpu_thread) {
-        stcpu_thread = rrdset_create("netdata", "plugin_proc_cpu", NULL, "proc", NULL,
-                                     "NetData Proc Plugin CPU usage", "milliseconds/s", 132000, rrd_update_every,
-                                     RRDSET_TYPE_STACKED);
+        stcpu_thread = rrdset_create_localhost("netdata", "plugin_proc_cpu", NULL, "proc", NULL
+                                               , "NetData Proc Plugin CPU usage", "milliseconds/s", 132000
+                                               , localhost->rrd_update_every, RRDSET_TYPE_STACKED);
 
-        rrddim_add(stcpu_thread, "user", NULL, 1, 1000, RRDDIM_INCREMENTAL);
-        rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);
+        rrddim_add(stcpu_thread, "user", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL);
+        rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL);
     } else rrdset_next(stcpu_thread);
 
     rrddim_set(stcpu_thread, "user", thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec);
@@ -145,13 +145,13 @@ void global_statistics_charts(void) {
 
     // ----------------------------------------------------------------
 
-    if (!stcpu) stcpu = rrdset_find("netdata.server_cpu");
+    if (!stcpu) stcpu = rrdset_find_localhost("netdata.server_cpu");
     if (!stcpu) {
-        stcpu = rrdset_create("netdata", "server_cpu", NULL, "netdata", NULL, "NetData CPU usage", "milliseconds/s",
-                              130000, rrd_update_every, RRDSET_TYPE_STACKED);
+        stcpu = rrdset_create_localhost("netdata", "server_cpu", NULL, "netdata", NULL, "NetData CPU usage"
+                                        , "milliseconds/s", 130000, localhost->rrd_update_every, RRDSET_TYPE_STACKED);
 
-        rrddim_add(stcpu, "user", NULL, 1, 1000, RRDDIM_INCREMENTAL);
-        rrddim_add(stcpu, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);
+        rrddim_add(stcpu, "user", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL);
+        rrddim_add(stcpu, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL);
     } else rrdset_next(stcpu);
 
     rrddim_set(stcpu, "user", me.ru_utime.tv_sec * 1000000ULL + me.ru_utime.tv_usec);
@@ -160,12 +160,12 @@ void global_statistics_charts(void) {
 
     // ----------------------------------------------------------------
 
-    if (!stclients) stclients = rrdset_find("netdata.clients");
+    if (!stclients) stclients = rrdset_find_localhost("netdata.clients");
     if (!stclients) {
-        stclients = rrdset_create("netdata", "clients", NULL, "netdata", NULL, "NetData Web Clients",
-                                  "connected clients", 130200, rrd_update_every, RRDSET_TYPE_LINE);
+        stclients = rrdset_create_localhost("netdata", "clients", NULL, "netdata", NULL, "NetData Web Clients"
+                                            , "connected clients", 130200, localhost->rrd_update_every, RRDSET_TYPE_LINE);
 
-        rrddim_add(stclients, "clients", NULL, 1, 1, RRDDIM_ABSOLUTE);
+        rrddim_add(stclients, "clients", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
     } else rrdset_next(stclients);
 
     rrddim_set(stclients, "clients", gs.connected_clients);
@@ -173,12 +173,12 @@ void global_statistics_charts(void) {
 
     // ----------------------------------------------------------------
 
-    if (!streqs) streqs = rrdset_find("netdata.requests");
+    if (!streqs) streqs = rrdset_find_localhost("netdata.requests");
     if (!streqs) {
-        streqs = rrdset_create("netdata", "requests", NULL, "netdata", NULL, "NetData Web Requests", "requests/s",
-                               130300, rrd_update_every, RRDSET_TYPE_LINE);
+        streqs = rrdset_create_localhost("netdata", "requests", NULL, "netdata", NULL, "NetData Web Requests"
+                                         , "requests/s", 130300, localhost->rrd_update_every, RRDSET_TYPE_LINE);
 
-        rrddim_add(streqs, "requests", NULL, 1, 1, RRDDIM_INCREMENTAL);
+        rrddim_add(streqs, "requests", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
     } else rrdset_next(streqs);
 
     rrddim_set(streqs, "requests", (collected_number) gs.web_requests);
@@ -186,13 +186,13 @@ void global_statistics_charts(void) {
 
     // ----------------------------------------------------------------
 
-    if (!stbytes) stbytes = rrdset_find("netdata.net");
+    if (!stbytes) stbytes = rrdset_find_localhost("netdata.net");
     if (!stbytes) {
-        stbytes = rrdset_create("netdata", "net", NULL, "netdata", NULL, "NetData Network Traffic", "kilobits/s",
-                                130000, rrd_update_every, RRDSET_TYPE_AREA);
+        stbytes = rrdset_create_localhost("netdata", "net", NULL, "netdata", NULL, "NetData Network Traffic"
+                                          , "kilobits/s", 130000, localhost->rrd_update_every, RRDSET_TYPE_AREA);
 
-        rrddim_add(stbytes, "in", NULL, 8, 1024, RRDDIM_INCREMENTAL);
-        rrddim_add(stbytes, "out", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+        rrddim_add(stbytes, "in", NULL, 8, 1024, RRD_ALGORITHM_INCREMENTAL);
+        rrddim_add(stbytes, "out", NULL, -8, 1024, RRD_ALGORITHM_INCREMENTAL);
     } else rrdset_next(stbytes);
 
     rrddim_set(stbytes, "in", (collected_number) gs.bytes_received);
@@ -201,13 +201,14 @@ void global_statistics_charts(void) {
 
     // ----------------------------------------------------------------
 
-    if (!stduration) stduration = rrdset_find("netdata.response_time");
+    if (!stduration) stduration = rrdset_find_localhost("netdata.response_time");
     if (!stduration) {
-        stduration = rrdset_create("netdata", "response_time", NULL, "netdata", NULL, "NetData API Response Time",
-                                   "ms/request", 130400, rrd_update_every, RRDSET_TYPE_LINE);
+        stduration = rrdset_create_localhost("netdata", "response_time", NULL, "netdata", NULL
+                                             , "NetData API Response Time", "ms/request", 130400, localhost->rrd_update_every
+                                             , RRDSET_TYPE_LINE);
 
-        rrddim_add(stduration, "average", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-        rrddim_add(stduration, "max", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+        rrddim_add(stduration, "average", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
+        rrddim_add(stduration, "max", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
     } else rrdset_next(stduration);
 
     uint64_t gweb_usec = gs.web_usec;
@@ -232,13 +233,13 @@ void global_statistics_charts(void) {
 
     // ----------------------------------------------------------------
 
-    if (!stcompression) stcompression = rrdset_find("netdata.compression_ratio");
+    if (!stcompression) stcompression = rrdset_find_localhost("netdata.compression_ratio");
     if (!stcompression) {
-        stcompression = rrdset_create("netdata", "compression_ratio", NULL, "netdata", NULL,
-                                      "NetData API Responses Compression Savings Ratio", "percentage", 130500,
-                                      rrd_update_every, RRDSET_TYPE_LINE);
+        stcompression = rrdset_create_localhost("netdata", "compression_ratio", NULL, "netdata", NULL
+                                                , "NetData API Responses Compression Savings Ratio", "percentage"
+                                                , 130500, localhost->rrd_update_every, RRDSET_TYPE_LINE);
 
-        rrddim_add(stcompression, "savings", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+        rrddim_add(stcompression, "savings", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
     } else rrdset_next(stcompression);
 
     // since we don't lock here to read the global statistics
index 9df2e241fe9619406c2a3a45138866dbd22765e7..0c7983ec1b588e68c5b1db38c92cdd0a304a7626 100644 (file)
+#define NETDATA_HEALTH_INTERNALS
 #include "common.h"
 
-#define RRDVAR_MAX_LENGTH 1024
+int default_health_enabled = 1;
 
-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;
-};
-
-static struct health_options health = {
-    .health_default_exec = NULL,
-    .health_default_recipient = "root",
-    .log_filename = NULL,
-    .log_entries_written = 0,
-    .log_fp = NULL
-};
-
-int health_enabled = 1;
-
-// ----------------------------------------------------------------------------
-// health alarm log load/save
-// no need for locking - only one thread is reading / writing the alarms log
-
-static inline int health_alarm_log_open(void) {
-    if(health.log_fp)
-        fclose(health.log_fp);
-
-    health.log_fp = fopen(health.log_filename, "a");
-
-    if(health.log_fp) {
-        if (setvbuf(health.log_fp, NULL, _IOLBF, 0) != 0)
-            error("Health: cannot set line buffering on health log file.");
-        return 0;
-    }
-
-    error("Health: cannot open health log file '%s'. Health data will be lost in case of netdata or server crash.", health.log_filename);
-    return -1;
-}
-
-static inline void health_alarm_log_close(void) {
-    if(health.log_fp) {
-        fclose(health.log_fp);
-        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("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) {
-    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;
-
-    errno = 0;
-
-    char *s, *buf = mallocz(65536 + 1);
-    size_t line = 0, len = 0;
-    ssize_t loaded = 0, updated = 0, errored = 0, 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);
-
-            freez(ae->name);
-            ae->name = strdupz(pointers[13]);
-            ae->hash_name = simple_hash(ae->name);
-
-            freez(ae->chart);
-            ae->chart = strdupz(pointers[14]);
-            ae->hash_chart = simple_hash(ae->chart);
-
-            freez(ae->family);
-            ae->family = strdupz(pointers[15]);
-
-            freez(ae->exec);
-            ae->exec = strdupz(pointers[16]);
-            if(!*ae->exec) { freez(ae->exec); ae->exec = NULL; }
-
-            freez(ae->recipient);
-            ae->recipient = strdupz(pointers[17]);
-            if(!*ae->recipient) { freez(ae->recipient); ae->recipient = NULL; }
-
-            freez(ae->source);
-            ae->source = strdupz(pointers[18]);
-            if(!*ae->source) { freez(ae->source); ae->source = NULL; }
-
-            freez(ae->units);
-            ae->units = strdupz(pointers[19]);
-            if(!*ae->units) { freez(ae->units); ae->units = NULL; }
-
-            freez(ae->info);
-            ae->info = strdupz(pointers[20]);
-            if(!*ae->info) { freez(ae->info); ae->info = NULL; }
-
-            ae->exec_code   = str2i(pointers[21]);
-            ae->new_status  = str2i(pointers[22]);
-            ae->old_status  = str2i(pointers[23]);
-            ae->delay       = str2i(pointers[24]);
-
-            ae->new_value   = str2l(pointers[25]);
-            ae->old_value   = str2l(pointers[26]);
-
-            static char value_string[100 + 1];
-            freez(ae->old_value_string);
-            freez(ae->new_value_string);
-            ae->old_value_string = strdupz(format_value_and_unit(value_string, 100, ae->old_value, ae->units, -1));
-            ae->new_value_string = strdupz(format_value_and_unit(value_string, 100, ae->new_value, ae->units, -1));
-
-            // 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)now_realtime_sec();
-    if(!max_alarm_id)  max_alarm_id  = (uint32_t)now_realtime_sec();
-
-    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) {
-    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
-
-static inline void health_alarm_log(
-        RRDHOST *host,
-        uint32_t alarm_id,
-        uint32_t alarm_event_id,
-        time_t when,
-        const char *name,
-        const char *chart,
-        const char *family,
-        const char *exec,
-        const char *recipient,
-        time_t duration,
-        calculated_number old_value,
-        calculated_number new_value,
-        int old_status,
-        int new_status,
-        const char *source,
-        const char *units,
-        const char *info,
-        int delay,
-        uint32_t flags
-) {
-    debug(D_HEALTH, "Health adding alarm log entry with id: %u", host->health_log.next_log_id);
-
-    ALARM_ENTRY *ae = callocz(1, sizeof(ALARM_ENTRY));
-    ae->name = strdupz(name);
-    ae->hash_name = simple_hash(ae->name);
-
-    if(chart) {
-        ae->chart = strdupz(chart);
-        ae->hash_chart = simple_hash(ae->chart);
-    }
-
-    if(family)
-        ae->family = strdupz(family);
-
-    if(exec) ae->exec = strdupz(exec);
-    if(recipient) ae->recipient = strdupz(recipient);
-    if(source) ae->source = strdupz(source);
-    if(units) ae->units = strdupz(units);
-    if(info) ae->info = strdupz(info);
-
-    ae->unique_id = host->health_log.next_log_id++;
-    ae->alarm_id = alarm_id;
-    ae->alarm_event_id = alarm_event_id;
-    ae->when = when;
-    ae->old_value = old_value;
-    ae->new_value = new_value;
-
-    static char value_string[100 + 1];
-    ae->old_value_string = strdupz(format_value_and_unit(value_string, 100, ae->old_value, ae->units, -1));
-    ae->new_value_string = strdupz(format_value_and_unit(value_string, 100, ae->new_value, ae->units, -1));
-
-    ae->old_status = old_status;
-    ae->new_status = new_status;
-    ae->duration = duration;
-    ae->delay = delay;
-    ae->delay_up_to_timestamp = when + delay;
-
-    ae->flags |= flags;
-
-    if(ae->old_status == RRDCALC_STATUS_WARNING || ae->old_status == RRDCALC_STATUS_CRITICAL)
-        ae->non_clear_duration += ae->duration;
-
-    // link it
-    pthread_rwlock_wrlock(&host->health_log.alarm_log_rwlock);
-    ae->next = host->health_log.alarms;
-    host->health_log.alarms = ae;
-    host->health_log.count++;
-    pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock);
-
-    // match previous alarms
-    pthread_rwlock_rdlock(&host->health_log.alarm_log_rwlock);
-    ALARM_ENTRY *t;
-    for(t = host->health_log.alarms ; t ; t = t->next) {
-        if(t != ae && t->alarm_id == ae->alarm_id) {
-            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;
-
-                if((t->new_status == RRDCALC_STATUS_WARNING || t->new_status == RRDCALC_STATUS_CRITICAL) &&
-                   (t->old_status == RRDCALC_STATUS_WARNING || t->old_status == RRDCALC_STATUS_CRITICAL))
-                    ae->non_clear_duration += t->non_clear_duration;
-
-                health_alarm_log_save(host, t);
-            }
-
-            // no need to continue
-            break;
-        }
-    }
-    pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock);
-
-    health_alarm_log_save(host, ae);
-}
-
-// ----------------------------------------------------------------------------
-// RRDVAR management
-
-static inline int rrdvar_fix_name(char *variable) {
-    int fixed = 0;
-    while(*variable) {
-        if (!isalnum(*variable) && *variable != '.' && *variable != '_') {
-            *variable++ = '_';
-            fixed++;
-        }
-        else
-            variable++;
-    }
-
-    return fixed;
-}
-
-int rrdvar_compare(void* a, void* b) {
-    if(((RRDVAR *)a)->hash < ((RRDVAR *)b)->hash) return -1;
-    else if(((RRDVAR *)a)->hash > ((RRDVAR *)b)->hash) return 1;
-    else return strcmp(((RRDVAR *)a)->name, ((RRDVAR *)b)->name);
-}
-
-static inline RRDVAR *rrdvar_index_add(avl_tree_lock *tree, RRDVAR *rv) {
-    RRDVAR *ret = (RRDVAR *)avl_insert_lock(tree, (avl *)(rv));
-    if(ret != rv)
-        debug(D_VARIABLES, "Request to insert RRDVAR '%s' into index failed. Already exists.", rv->name);
-
-    return ret;
-}
-
-static inline RRDVAR *rrdvar_index_del(avl_tree_lock *tree, RRDVAR *rv) {
-    RRDVAR *ret = (RRDVAR *)avl_remove_lock(tree, (avl *)(rv));
-    if(!ret)
-        error("Request to remove RRDVAR '%s' from index failed. Not Found.", rv->name);
-
-    return ret;
-}
-
-static inline RRDVAR *rrdvar_index_find(avl_tree_lock *tree, const char *name, uint32_t hash) {
-    RRDVAR tmp;
-    tmp.name = (char *)name;
-    tmp.hash = (hash)?hash:simple_hash(tmp.name);
-
-    return (RRDVAR *)avl_search_lock(tree, (avl *)&tmp);
-}
-
-static inline void rrdvar_free(RRDHOST *host, avl_tree_lock *tree, RRDVAR *rv) {
-    (void)host;
-
-    if(!rv) return;
-
-    if(tree) {
-        debug(D_VARIABLES, "Deleting variable '%s'", rv->name);
-        if(unlikely(!rrdvar_index_del(tree, rv)))
-            error("Attempted to delete variable '%s' from host '%s', but it is not found.", rv->name, host->hostname);
-    }
-
-    freez(rv->name);
-    freez(rv);
-}
-
-static inline RRDVAR *rrdvar_create_and_index(const char *scope, avl_tree_lock *tree, const char *name, int type, void *value) {
-    char *variable = strdupz(name);
-    rrdvar_fix_name(variable);
-    uint32_t hash = simple_hash(variable);
-
-    RRDVAR *rv = rrdvar_index_find(tree, variable, hash);
-    if(unlikely(!rv)) {
-        debug(D_VARIABLES, "Variable '%s' not found in scope '%s'. Creating a new one.", variable, scope);
-
-        rv = callocz(1, sizeof(RRDVAR));
-        rv->name = variable;
-        rv->hash = hash;
-        rv->type = type;
-        rv->value = value;
-
-        RRDVAR *ret = rrdvar_index_add(tree, rv);
-        if(unlikely(ret != rv)) {
-            debug(D_VARIABLES, "Variable '%s' in scope '%s' already exists", variable, scope);
-            rrdvar_free(NULL, NULL, rv);
-            rv = NULL;
-        }
-        else
-            debug(D_VARIABLES, "Variable '%s' created in scope '%s'", variable, scope);
-    }
-    else {
-        debug(D_VARIABLES, "Variable '%s' is already found in scope '%s'.", variable, scope);
-
-        // already exists
-        freez(variable);
-
-        // this is important
-        // it must return NULL - not the existing variable - or double-free will happen
-        rv = NULL;
-    }
-
-    return rv;
-}
-
-// ----------------------------------------------------------------------------
-// CUSTOM VARIABLES
-
-RRDVAR *rrdvar_custom_host_variable_create(RRDHOST *host, const char *name) {
-    calculated_number *v = callocz(1, sizeof(calculated_number));
-    *v = NAN;
-    RRDVAR *rv = rrdvar_create_and_index("host", &host->variables_root_index, name, RRDVAR_TYPE_CALCULATED_ALLOCATED, v);
-    if(unlikely(!rv)) {
-        free(v);
-        error("Requested variable '%s' already exists - possibly 2 plugins will be updating it at the same time", name);
-
-        char *variable = strdupz(name);
-        rrdvar_fix_name(variable);
-        uint32_t hash = simple_hash(variable);
-
-        rv = rrdvar_index_find(&host->variables_root_index, variable, hash);
-    }
-
-    return rv;
-}
-
-void rrdvar_custom_host_variable_destroy(RRDHOST *host, const char *name) {
-    char *variable = strdupz(name);
-    rrdvar_fix_name(variable);
-    uint32_t hash = simple_hash(variable);
-
-    RRDVAR *rv = rrdvar_index_find(&host->variables_root_index, variable, hash);
-    freez(variable);
-
-    if(!rv) {
-        error("Attempted to remove variable '%s' from host '%s', but it does not exist.", name, host->hostname);
-        return;
-    }
-
-    if(rv->type != RRDVAR_TYPE_CALCULATED_ALLOCATED) {
-        error("Attempted to remove variable '%s' from host '%s', but it does not a custom allocated variable.", name, host->hostname);
-        return;
-    }
-
-    if(!rrdvar_index_del(&host->variables_root_index, rv)) {
-        error("Attempted to remove variable '%s' from host '%s', but it cannot be found.", name, host->hostname);
-        return;
-    }
-
-    freez(rv->name);
-    freez(rv->value);
-    freez(rv);
-}
-
-void rrdvar_custom_host_variable_set(RRDVAR *rv, calculated_number value) {
-    if(rv->type != RRDVAR_TYPE_CALCULATED_ALLOCATED)
-        error("requested to set variable '%s' to value " CALCULATED_NUMBER_FORMAT " but the variable is not a custom one.", rv->name, value);
-    else {
-        calculated_number *v = rv->value;
-        *v = value;
-    }
-}
-
-// ----------------------------------------------------------------------------
-// RRDVAR lookup
-
-static calculated_number rrdvar2number(RRDVAR *rv) {
-    switch(rv->type) {
-        case RRDVAR_TYPE_CALCULATED_ALLOCATED:
-        case RRDVAR_TYPE_CALCULATED: {
-            calculated_number *n = (calculated_number *)rv->value;
-            return *n;
-        }
-
-        case RRDVAR_TYPE_TIME_T: {
-            time_t *n = (time_t *)rv->value;
-            return *n;
-        }
-
-        case RRDVAR_TYPE_COLLECTED: {
-            collected_number *n = (collected_number *)rv->value;
-            return *n;
-        }
-
-        case RRDVAR_TYPE_TOTAL: {
-            total_number *n = (total_number *)rv->value;
-            return *n;
-        }
-
-        case RRDVAR_TYPE_INT: {
-            int *n = (int *)rv->value;
-            return *n;
-        }
-
-        default:
-            error("I don't know how to convert RRDVAR type %d to calculated_number", rv->type);
-            return NAN;
-    }
-}
-
-int health_variable_lookup(const char *variable, uint32_t hash, RRDCALC *rc, calculated_number *result) {
-    RRDSET *st = rc->rrdset;
-    RRDVAR *rv;
-
-    if(!st) return 0;
-
-    rv = rrdvar_index_find(&st->variables_root_index, variable, hash);
-    if(rv) {
-        *result = rrdvar2number(rv);
-        return 1;
-    }
-
-    rv = rrdvar_index_find(&st->rrdfamily->variables_root_index, variable, hash);
-    if(rv) {
-        *result = rrdvar2number(rv);
-        return 1;
-    }
-
-    rv = rrdvar_index_find(&st->rrdhost->variables_root_index, variable, hash);
-    if(rv) {
-        *result = rrdvar2number(rv);
-        return 1;
-    }
-
-    return 0;
-}
-
-// ----------------------------------------------------------------------------
-// RRDVAR to JSON
-
-struct variable2json_helper {
-    BUFFER *buf;
-    size_t counter;
-};
-
-static int single_variable2json(void *entry, void *data) {
-    struct variable2json_helper *helper = (struct variable2json_helper *)data;
-    RRDVAR *rv = (RRDVAR *)entry;
-    calculated_number value = rrdvar2number(rv);
-
-    if(unlikely(isnan(value) || isinf(value)))
-        buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": null", helper->counter?",":"", rv->name);
-    else
-        buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": %0.5Lf", helper->counter?",":"", rv->name, (long double)value);
-
-    helper->counter++;
-
-    return 0;
-}
-
-void health_api_v1_chart_variables2json(RRDSET *st, BUFFER *buf) {
-    struct variable2json_helper helper = {
-            .buf = buf,
-            .counter = 0
-    };
-
-    buffer_sprintf(buf, "{\n\t\"chart\": \"%s\",\n\t\"chart_name\": \"%s\",\n\t\"chart_context\": \"%s\",\n\t\"chart_variables\": {", st->id, st->name, st->context);
-    avl_traverse_lock(&st->variables_root_index, single_variable2json, (void *)&helper);
-    buffer_sprintf(buf, "\n\t},\n\t\"family\": \"%s\",\n\t\"family_variables\": {", st->family);
-    helper.counter = 0;
-    avl_traverse_lock(&st->rrdfamily->variables_root_index, single_variable2json, (void *)&helper);
-    buffer_sprintf(buf, "\n\t},\n\t\"host\": \"%s\",\n\t\"host_variables\": {", st->rrdhost->hostname);
-    helper.counter = 0;
-    avl_traverse_lock(&st->rrdhost->variables_root_index, single_variable2json, (void *)&helper);
-    buffer_strcat(buf, "\n\t}\n}\n");
-}
-
-
-// ----------------------------------------------------------------------------
-// RRDDIMVAR management
-// DIMENSION VARIABLES
-
-#define RRDDIMVAR_ID_MAX 1024
-
-static inline void rrddimvar_free_variables(RRDDIMVAR *rs) {
-    RRDDIM *rd = rs->rrddim;
-    RRDSET *st = rd->rrdset;
-
-    // CHART VARIABLES FOR THIS DIMENSION
-
-    rrdvar_free(st->rrdhost, &st->variables_root_index, rs->var_local_id);
-    rs->var_local_id = NULL;
-
-    rrdvar_free(st->rrdhost, &st->variables_root_index, rs->var_local_name);
-    rs->var_local_name = NULL;
-
-    // FAMILY VARIABLES FOR THIS DIMENSION
-
-    rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_id);
-    rs->var_family_id = NULL;
-
-    rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_name);
-    rs->var_family_name = NULL;
-
-    rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_contextid);
-    rs->var_family_contextid = NULL;
-
-    rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_contextname);
-    rs->var_family_contextname = NULL;
-
-    // HOST VARIABLES FOR THIS DIMENSION
-
-    rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartidid);
-    rs->var_host_chartidid = NULL;
-
-    rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartidname);
-    rs->var_host_chartidname = NULL;
-
-    rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartnameid);
-    rs->var_host_chartnameid = NULL;
-
-    rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartnamename);
-    rs->var_host_chartnamename = NULL;
-
-    // KEYS
-
-    freez(rs->key_id);
-    rs->key_id = NULL;
-
-    freez(rs->key_name);
-    rs->key_name = NULL;
-
-    freez(rs->key_fullidid);
-    rs->key_fullidid = NULL;
-
-    freez(rs->key_fullidname);
-    rs->key_fullidname = NULL;
-
-    freez(rs->key_contextid);
-    rs->key_contextid = NULL;
-
-    freez(rs->key_contextname);
-    rs->key_contextname = NULL;
-
-    freez(rs->key_fullnameid);
-    rs->key_fullnameid = NULL;
-
-    freez(rs->key_fullnamename);
-    rs->key_fullnamename = NULL;
-}
-
-static inline void rrddimvar_create_variables(RRDDIMVAR *rs) {
-    rrddimvar_free_variables(rs);
-
-    RRDDIM *rd = rs->rrddim;
-    RRDSET *st = rd->rrdset;
-
-    char buffer[RRDDIMVAR_ID_MAX + 1];
-
-    // KEYS
-
-    snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->id, rs->suffix);
-    rs->key_id = strdupz(buffer);
-
-    snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->name, rs->suffix);
-    rs->key_name = strdupz(buffer);
-
-    snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->key_id);
-    rs->key_fullidid = strdupz(buffer);
-
-    snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->key_name);
-    rs->key_fullidname = strdupz(buffer);
-
-    snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->context, rs->key_id);
-    rs->key_contextid = strdupz(buffer);
-
-    snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->context, rs->key_name);
-    rs->key_contextname = strdupz(buffer);
-
-    snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->key_id);
-    rs->key_fullnameid = strdupz(buffer);
-
-    snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->key_name);
-    rs->key_fullnamename = strdupz(buffer);
-
-    // CHART VARIABLES FOR THIS DIMENSION
-    // -----------------------------------
-    //
-    // dimensions are available as:
-    // - $id
-    // - $name
-
-    rs->var_local_id           = rrdvar_create_and_index("local", &st->variables_root_index, rs->key_id, rs->type, rs->value);
-    rs->var_local_name         = rrdvar_create_and_index("local", &st->variables_root_index, rs->key_name, rs->type, rs->value);
-
-    // FAMILY VARIABLES FOR THIS DIMENSION
-    // -----------------------------------
-    //
-    // dimensions are available as:
-    // - $id                 (only the first, when multiple overlap)
-    // - $name               (only the first, when multiple overlap)
-    // - $chart-context.id
-    // - $chart-context.name
-
-    rs->var_family_id          = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_id, rs->type, rs->value);
-    rs->var_family_name        = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_name, rs->type, rs->value);
-    rs->var_family_contextid   = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_contextid, rs->type, rs->value);
-    rs->var_family_contextname = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_contextname, rs->type, rs->value);
-
-    // HOST VARIABLES FOR THIS DIMENSION
-    // -----------------------------------
-    //
-    // dimensions are available as:
-    // - $chart-id.id
-    // - $chart-id.name
-    // - $chart-name.id
-    // - $chart-name.name
-
-    rs->var_host_chartidid      = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullidid, rs->type, rs->value);
-    rs->var_host_chartidname    = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullidname, rs->type, rs->value);
-    rs->var_host_chartnameid    = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullnameid, rs->type, rs->value);
-    rs->var_host_chartnamename  = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullnamename, rs->type, rs->value);
-}
-
-RRDDIMVAR *rrddimvar_create(RRDDIM *rd, int type, const char *prefix, const char *suffix, void *value, uint32_t options) {
-    RRDSET *st = rd->rrdset;
-
-    debug(D_VARIABLES, "RRDDIMSET create for chart id '%s' name '%s', dimension id '%s', name '%s%s%s'", st->id, st->name, rd->id, (prefix)?prefix:"", rd->name, (suffix)?suffix:"");
-
-    if(!prefix) prefix = "";
-    if(!suffix) suffix = "";
-
-    RRDDIMVAR *rs = (RRDDIMVAR *)callocz(1, sizeof(RRDDIMVAR));
-
-    rs->prefix = strdupz(prefix);
-    rs->suffix = strdupz(suffix);
-
-    rs->type = type;
-    rs->value = value;
-    rs->options = options;
-    rs->rrddim = rd;
-
-    rs->next = rd->variables;
-    rd->variables = rs;
-
-    rrddimvar_create_variables(rs);
-
-    return rs;
-}
-
-void rrddimvar_rename_all(RRDDIM *rd) {
-    RRDSET *st = rd->rrdset;
-    debug(D_VARIABLES, "RRDDIMSET rename for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name);
-
-    RRDDIMVAR *rs, *next = rd->variables;
-    while((rs = next)) {
-        next = rs->next;
-        rrddimvar_create_variables(rs);
-    }
-}
-
-void rrddimvar_free(RRDDIMVAR *rs) {
-    RRDDIM *rd = rs->rrddim;
-    RRDSET *st = rd->rrdset;
-    debug(D_VARIABLES, "RRDDIMSET free for chart id '%s' name '%s', dimension id '%s', name '%s', prefix='%s', suffix='%s'", st->id, st->name, rd->id, rd->name, rs->prefix, rs->suffix);
-
-    rrddimvar_free_variables(rs);
-
-    if(rd->variables == rs) {
-        debug(D_VARIABLES, "RRDDIMSET removing first entry for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name);
-        rd->variables = rs->next;
-    }
-    else {
-        debug(D_VARIABLES, "RRDDIMSET removing non-first entry for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name);
-        RRDDIMVAR *t;
-        for (t = rd->variables; t && t->next != rs; t = t->next) ;
-        if(!t) error("RRDDIMVAR '%s' not found in dimension '%s/%s' variables linked list", rs->key_name, st->id, rd->id);
-        else t->next = rs->next;
-    }
-
-    freez(rs->prefix);
-    freez(rs->suffix);
-    freez(rs);
-}
-
-// ----------------------------------------------------------------------------
-// RRDSETVAR management
-// CHART VARIABLES
-
-static inline void rrdsetvar_free_variables(RRDSETVAR *rs) {
-    RRDSET *st = rs->rrdset;
-
-    // CHART
-
-    rrdvar_free(st->rrdhost, &st->variables_root_index, rs->var_local);
-    rs->var_local = NULL;
-
-    // FAMILY
-
-    rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family);
-    rs->var_family = NULL;
-
-    rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host);
-    rs->var_host = NULL;
-
-    // HOST
-
-    rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_name);
-    rs->var_family_name = NULL;
-
-    rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_name);
-    rs->var_host_name = NULL;
-
-    // KEYS
-
-    freez(rs->key_fullid);
-    rs->key_fullid = NULL;
-
-    freez(rs->key_fullname);
-    rs->key_fullname = NULL;
-}
-
-static inline void rrdsetvar_create_variables(RRDSETVAR *rs) {
-    rrdsetvar_free_variables(rs);
-
-    RRDSET *st = rs->rrdset;
-
-    // KEYS
-
-    char buffer[RRDVAR_MAX_LENGTH + 1];
-    snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->id, rs->variable);
-    rs->key_fullid = strdupz(buffer);
-
-    snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->name, rs->variable);
-    rs->key_fullname = strdupz(buffer);
-
-    // CHART
-
-    rs->var_local       = rrdvar_create_and_index("local",  &st->variables_root_index,               rs->variable, rs->type, rs->value);
-
-    // FAMILY
-
-    rs->var_family      = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index,    rs->key_fullid,   rs->type, rs->value);
-    rs->var_family_name = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index,    rs->key_fullname, rs->type, rs->value);
-
-    // HOST
-
-    rs->var_host        = rrdvar_create_and_index("host",   &st->rrdhost->variables_root_index,      rs->key_fullid,   rs->type, rs->value);
-    rs->var_host_name   = rrdvar_create_and_index("host",   &st->rrdhost->variables_root_index,      rs->key_fullname, rs->type, rs->value);
-
-}
-
-RRDSETVAR *rrdsetvar_create(RRDSET *st, const char *variable, int type, void *value, uint32_t options) {
-    debug(D_VARIABLES, "RRDVARSET create for chart id '%s' name '%s' with variable name '%s'", st->id, st->name, variable);
-    RRDSETVAR *rs = (RRDSETVAR *)callocz(1, sizeof(RRDSETVAR));
-
-    rs->variable = strdupz(variable);
-    rs->type = type;
-    rs->value = value;
-    rs->options = options;
-    rs->rrdset = st;
-
-    rs->next = st->variables;
-    st->variables = rs;
-
-    rrdsetvar_create_variables(rs);
-
-    return rs;
-}
-
-void rrdsetvar_rename_all(RRDSET *st) {
-    debug(D_VARIABLES, "RRDSETVAR rename for chart id '%s' name '%s'", st->id, st->name);
-
-    RRDSETVAR *rs, *next = st->variables;
-    while((rs = next)) {
-        next = rs->next;
-        rrdsetvar_create_variables(rs);
-    }
-
-    rrdsetcalc_link_matching(st);
-}
-
-void rrdsetvar_free(RRDSETVAR *rs) {
-    RRDSET *st = rs->rrdset;
-    debug(D_VARIABLES, "RRDSETVAR free for chart id '%s' name '%s', variable '%s'", st->id, st->name, rs->variable);
-
-    if(st->variables == rs) {
-        st->variables = rs->next;
-    }
-    else {
-        RRDSETVAR *t;
-        for (t = st->variables; t && t->next != rs; t = t->next);
-        if(!t) error("RRDSETVAR '%s' not found in chart '%s' variables linked list", rs->key_fullname, st->id);
-        else t->next = rs->next;
-    }
-
-    rrdsetvar_free_variables(rs);
-
-    freez(rs->variable);
-    freez(rs);
-}
-
-// ----------------------------------------------------------------------------
-// RRDCALC management
-
-inline const char *rrdcalc_status2string(int status) {
-    switch(status) {
-        case RRDCALC_STATUS_REMOVED:
-            return "REMOVED";
-
-        case RRDCALC_STATUS_UNDEFINED:
-            return "UNDEFINED";
-
-        case RRDCALC_STATUS_UNINITIALIZED:
-            return "UNINITIALIZED";
-
-        case RRDCALC_STATUS_CLEAR:
-            return "CLEAR";
-
-        case RRDCALC_STATUS_RAISED:
-            return "RAISED";
-
-        case RRDCALC_STATUS_WARNING:
-            return "WARNING";
-
-        case RRDCALC_STATUS_CRITICAL:
-            return "CRITICAL";
-
-        default:
-            error("Unknown alarm status %d", status);
-            return "UNKNOWN";
-    }
-}
-
-static void rrdsetcalc_link(RRDSET *st, RRDCALC *rc) {
-    debug(D_HEALTH, "Health linking alarm '%s.%s' to chart '%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, st->id, st->rrdhost->hostname);
-
-    rc->last_status_change = now_realtime_sec();
-    rc->rrdset = st;
-
-    rc->rrdset_next = st->alarms;
-    rc->rrdset_prev = NULL;
-    
-    if(rc->rrdset_next)
-        rc->rrdset_next->rrdset_prev = rc;
-
-    st->alarms = rc;
-
-    if(rc->update_every < rc->rrdset->update_every) {
-        error("Health alarm '%s.%s' has update every %d, less than chart update every %d. Setting alarm update frequency to %d.", rc->rrdset->id, rc->name, rc->update_every, rc->rrdset->update_every, rc->rrdset->update_every);
-        rc->update_every = rc->rrdset->update_every;
-    }
-
-    if(!isnan(rc->green) && isnan(st->green)) {
-        debug(D_HEALTH, "Health alarm '%s.%s' green threshold set from %Lf to %Lf.", rc->rrdset->id, rc->name, rc->rrdset->green, rc->green);
-        st->green = rc->green;
-    }
-
-    if(!isnan(rc->red) && isnan(st->red)) {
-        debug(D_HEALTH, "Health alarm '%s.%s' red threshold set from %Lf to %Lf.", rc->rrdset->id, rc->name, rc->rrdset->red, rc->red);
-        st->red = rc->red;
-    }
-
-    rc->local  = rrdvar_create_and_index("local",  &st->variables_root_index, rc->name, RRDVAR_TYPE_CALCULATED, &rc->value);
-    rc->family = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rc->name, RRDVAR_TYPE_CALCULATED, &rc->value);
-
-    char fullname[RRDVAR_MAX_LENGTH + 1];
-    snprintfz(fullname, RRDVAR_MAX_LENGTH, "%s.%s", st->id, rc->name);
-    rc->hostid   = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, fullname, RRDVAR_TYPE_CALCULATED, &rc->value);
-
-    snprintfz(fullname, RRDVAR_MAX_LENGTH, "%s.%s", st->name, rc->name);
-    rc->hostname = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, fullname, RRDVAR_TYPE_CALCULATED, &rc->value);
-
-       if(!rc->units) rc->units = strdupz(st->units);
-
-    {
-        time_t now = now_realtime_sec();
-        health_alarm_log(
-                st->rrdhost,
-                rc->id,
-                rc->next_event_id++,
-                now,
-                rc->name,
-                rc->rrdset->id,
-                rc->rrdset->family,
-                rc->exec,
-                rc->recipient,
-                now - rc->last_status_change,
-                rc->old_value,
-                rc->value,
-                rc->status,
-                RRDCALC_STATUS_UNINITIALIZED,
-                rc->source,
-                rc->units,
-                rc->info,
-                0,
-                0
-        );
-    }
-}
-
-static inline int rrdcalc_is_matching_this_rrdset(RRDCALC *rc, RRDSET *st) {
-    if(     (rc->hash_chart == st->hash      && !strcmp(rc->chart, st->id)) ||
-            (rc->hash_chart == st->hash_name && !strcmp(rc->chart, st->name)))
-        return 1;
-
-    return 0;
-}
-
-// this has to be called while the RRDHOST is locked
-inline void rrdsetcalc_link_matching(RRDSET *st) {
-    // debug(D_HEALTH, "find matching alarms for chart '%s'", st->id);
-
-    RRDCALC *rc;
-    for(rc = st->rrdhost->alarms; rc ; rc = rc->next) {
-        if(unlikely(rc->rrdset))
-            continue;
-
-        if(unlikely(rrdcalc_is_matching_this_rrdset(rc, st)))
-            rrdsetcalc_link(st, rc);
-    }
-}
-
-// this has to be called while the RRDHOST is locked
-inline void rrdsetcalc_unlink(RRDCALC *rc) {
-    RRDSET *st = rc->rrdset;
-
-    if(!st) {
-        debug(D_HEALTH, "Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rc->chart?rc->chart:"NOCHART", rc->name);
-        error("Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rc->chart?rc->chart:"NOCHART", rc->name);
-        return;
-    }
-
-    {
-        time_t now = now_realtime_sec();
-        health_alarm_log(
-                st->rrdhost,
-                rc->id,
-                rc->next_event_id++,
-                now,
-                rc->name,
-                rc->rrdset->id,
-                rc->rrdset->family,
-                rc->exec,
-                rc->recipient,
-                now - rc->last_status_change,
-                rc->old_value,
-                rc->value,
-                rc->status,
-                RRDCALC_STATUS_REMOVED,
-                rc->source,
-                rc->units,
-                rc->info,
-                0,
-                0
-        );
-    }
-
-    RRDHOST *host = st->rrdhost;
-
-    debug(D_HEALTH, "Health unlinking alarm '%s.%s' from chart '%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, st->id, host->hostname);
-
-    // unlink it
-    if(rc->rrdset_prev)
-        rc->rrdset_prev->rrdset_next = rc->rrdset_next;
-
-    if(rc->rrdset_next)
-        rc->rrdset_next->rrdset_prev = rc->rrdset_prev;
-
-    if(st->alarms == rc)
-        st->alarms = rc->rrdset_next;
-
-    rc->rrdset_prev = rc->rrdset_next = NULL;
-
-    rrdvar_free(st->rrdhost, &st->variables_root_index, rc->local);
-    rc->local = NULL;
-
-    rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rc->family);
-    rc->family = NULL;
-
-    rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rc->hostid);
-    rc->hostid = NULL;
-
-    rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rc->hostname);
-    rc->hostname = NULL;
-
-    rc->rrdset = NULL;
-
-    // RRDCALC will remain in RRDHOST
-    // so that if the matching chart is found in the future
-    // it will be applied automatically
-}
-
-RRDCALC *rrdcalc_find(RRDSET *st, const char *name) {
-    RRDCALC *rc;
-    uint32_t hash = simple_hash(name);
-
-    for( rc = st->alarms; rc ; rc = rc->rrdset_next ) {
-        if(unlikely(rc->hash == hash && !strcmp(rc->name, name)))
-            return rc;
-    }
-
-    return NULL;
-}
-
-static inline int rrdcalc_exists(RRDHOST *host, const char *chart, const char *name, uint32_t hash_chart, uint32_t hash_name) {
-    RRDCALC *rc;
-
-    if(unlikely(!chart)) {
-        error("attempt to find RRDCALC '%s' without giving a chart name", name);
-        return 1;
-    }
-
-    if(unlikely(!hash_chart)) hash_chart = simple_hash(chart);
-    if(unlikely(!hash_name))  hash_name  = simple_hash(name);
-
-    // make sure it does not already exist
-    for(rc = host->alarms; rc ; rc = rc->next) {
-        if (unlikely(rc->chart && rc->hash == hash_name && rc->hash_chart == hash_chart && !strcmp(name, rc->name) && !strcmp(chart, rc->chart))) {
-            debug(D_HEALTH, "Health alarm '%s.%s' already exists in host '%s'.", chart, name, host->hostname);
-            error("Health alarm '%s.%s' already exists in host '%s'.", chart, name, host->hostname);
-            return 1;
-        }
-    }
-
-    return 0;
-}
-
-static inline uint32_t rrdcalc_get_unique_id(RRDHOST *host, const char *chart, const char *name, uint32_t *next_event_id) {
-    if(chart && name) {
-        uint32_t hash_chart = simple_hash(chart);
-        uint32_t hash_name = simple_hash(name);
-
-        // re-use old IDs, by looking them up in the alarm log
-        ALARM_ENTRY *ae;
-        for(ae = host->health_log.alarms; ae ;ae = ae->next) {
-            if(unlikely(ae->hash_name == hash_name && ae->hash_chart == hash_chart && !strcmp(name, ae->name) && !strcmp(chart, ae->chart))) {
-                if(next_event_id) *next_event_id = ae->alarm_event_id + 1;
-                return ae->alarm_id;
-            }
-        }
-    }
-
-    return host->health_log.next_alarm_id++;
-}
-
-static inline void rrdcalc_create_part2(RRDHOST *host, RRDCALC *rc) {
-    rrdhost_check_rdlock(host);
-
-    if(rc->calculation) {
-        rc->calculation->status = &rc->status;
-        rc->calculation->this = &rc->value;
-        rc->calculation->after = &rc->db_after;
-        rc->calculation->before = &rc->db_before;
-        rc->calculation->rrdcalc = rc;
-    }
-
-    if(rc->warning) {
-        rc->warning->status = &rc->status;
-        rc->warning->this = &rc->value;
-        rc->warning->after = &rc->db_after;
-        rc->warning->before = &rc->db_before;
-        rc->warning->rrdcalc = rc;
-    }
-
-    if(rc->critical) {
-        rc->critical->status = &rc->status;
-        rc->critical->this = &rc->value;
-        rc->critical->after = &rc->db_after;
-        rc->critical->before = &rc->db_before;
-        rc->critical->rrdcalc = rc;
-    }
-
-    // link it to the host
-    if(likely(host->alarms)) {
-        // append it
-        RRDCALC *t;
-        for(t = host->alarms; t && t->next ; t = t->next) ;
-        t->next = rc;
-    }
-    else {
-        host->alarms = rc;
-    }
-
-    // link it to its chart
-    RRDSET *st;
-    for(st = host->rrdset_root; st ; st = st->next) {
-        if(rrdcalc_is_matching_this_rrdset(rc, st)) {
-            rrdsetcalc_link(st, rc);
-            break;
-        }
-    }
-}
-
-static inline RRDCALC *rrdcalc_create(RRDHOST *host, RRDCALCTEMPLATE *rt, const char *chart) {
-
-    debug(D_HEALTH, "Health creating dynamic alarm (from template) '%s.%s'", chart, rt->name);
-
-    if(rrdcalc_exists(host, chart, rt->name, 0, 0))
-        return NULL;
-
-    RRDCALC *rc = callocz(1, sizeof(RRDCALC));
-    rc->next_event_id = 1;
-    rc->id = rrdcalc_get_unique_id(host, chart, rt->name, &rc->next_event_id);
-    rc->name = strdupz(rt->name);
-    rc->hash = simple_hash(rc->name);
-    rc->chart = strdupz(chart);
-    rc->hash_chart = simple_hash(rc->chart);
-
-    if(rt->dimensions) rc->dimensions = strdupz(rt->dimensions);
-
-    rc->green = rt->green;
-    rc->red = rt->red;
-    rc->value = NAN;
-    rc->old_value = NAN;
-
-    rc->delay_up_duration = rt->delay_up_duration;
-    rc->delay_down_duration = rt->delay_down_duration;
-    rc->delay_max_duration = rt->delay_max_duration;
-    rc->delay_multiplier = rt->delay_multiplier;
-
-    rc->group = rt->group;
-    rc->after = rt->after;
-    rc->before = rt->before;
-    rc->update_every = rt->update_every;
-    rc->options = rt->options;
-
-    if(rt->exec) rc->exec = strdupz(rt->exec);
-    if(rt->recipient) rc->recipient = strdupz(rt->recipient);
-    if(rt->source) rc->source = strdupz(rt->source);
-    if(rt->units) rc->units = strdupz(rt->units);
-    if(rt->info) rc->info = strdupz(rt->info);
-
-    if(rt->calculation) {
-        rc->calculation = expression_parse(rt->calculation->source, NULL, NULL);
-        if(!rc->calculation)
-            error("Health alarm '%s.%s': failed to parse calculation expression '%s'", chart, rt->name, rt->calculation->source);
-    }
-    if(rt->warning) {
-        rc->warning = expression_parse(rt->warning->source, NULL, NULL);
-        if(!rc->warning)
-            error("Health alarm '%s.%s': failed to re-parse warning expression '%s'", chart, rt->name, rt->warning->source);
-    }
-    if(rt->critical) {
-        rc->critical = expression_parse(rt->critical->source, NULL, NULL);
-        if(!rc->critical)
-            error("Health alarm '%s.%s': failed to re-parse critical expression '%s'", chart, rt->name, rt->critical->source);
-    }
-
-    debug(D_HEALTH, "Health runtime added alarm '%s.%s': exec '%s', recipient '%s', green %Lf, red %Lf, lookup: group %d, after %d, before %d, options %u, dimensions '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s', delay up %d, delay down %d, delay max %d, delay_multiplier %f",
-          (rc->chart)?rc->chart:"NOCHART",
-          rc->name,
-          (rc->exec)?rc->exec:"DEFAULT",
-          (rc->recipient)?rc->recipient:"DEFAULT",
-          rc->green,
-          rc->red,
-          rc->group,
-          rc->after,
-          rc->before,
-          rc->options,
-          (rc->dimensions)?rc->dimensions:"NONE",
-          rc->update_every,
-          (rc->calculation)?rc->calculation->parsed_as:"NONE",
-          (rc->warning)?rc->warning->parsed_as:"NONE",
-          (rc->critical)?rc->critical->parsed_as:"NONE",
-          rc->source,
-          rc->delay_up_duration,
-          rc->delay_down_duration,
-          rc->delay_max_duration,
-          rc->delay_multiplier
-    );
-
-    rrdcalc_create_part2(host, rc);
-    return rc;
-}
-
-void rrdcalc_free(RRDHOST *host, RRDCALC *rc) {
-    if(!rc) return;
-
-    debug(D_HEALTH, "Health removing alarm '%s.%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, host->hostname);
-
-    // unlink it from RRDSET
-    if(rc->rrdset) rrdsetcalc_unlink(rc);
-
-    // unlink it from RRDHOST
-    if(unlikely(rc == host->alarms))
-        host->alarms = rc->next;
-
-    else if(likely(host->alarms)) {
-        RRDCALC *t, *last = host->alarms;
-        for(t = last->next; t && t != rc; last = t, t = t->next) ;
-        if(last->next == rc)
-            last->next = rc->next;
-        else
-            error("Cannot unlink alarm '%s.%s' from host '%s': not found", rc->chart?rc->chart:"NOCHART", rc->name, host->hostname);
-    }
-    else
-        error("Cannot unlink unlink '%s.%s' from host '%s': This host does not have any calculations", rc->chart?rc->chart:"NOCHART", rc->name, host->hostname);
-
-    expression_free(rc->calculation);
-    expression_free(rc->warning);
-    expression_free(rc->critical);
-
-    freez(rc->name);
-    freez(rc->chart);
-    freez(rc->family);
-    freez(rc->dimensions);
-    freez(rc->exec);
-    freez(rc->recipient);
-    freez(rc->source);
-    freez(rc->units);
-    freez(rc->info);
-    freez(rc);
-}
-
-// ----------------------------------------------------------------------------
-// RRDCALCTEMPLATE management
-
-void rrdcalctemplate_link_matching(RRDSET *st) {
-    RRDCALCTEMPLATE *rt;
-
-    for(rt = st->rrdhost->templates; rt ; rt = rt->next) {
-        if(rt->hash_context == st->hash_context && !strcmp(rt->context, st->context)
-                && (!rt->family_pattern || simple_pattern_matches(rt->family_pattern, st->family))) {
-            RRDCALC *rc = rrdcalc_create(st->rrdhost, rt, st->id);
-            if(unlikely(!rc))
-                error("Health tried to create alarm from template '%s', but it failed", rt->name);
-
-#ifdef NETDATA_INTERNAL_CHECKS
-            else if(rc->rrdset != st)
-                error("Health alarm '%s.%s' should be linked to chart '%s', but it is not", rc->chart?rc->chart:"NOCHART", rc->name, st->id);
-#endif
-        }
-    }
-}
-
-static inline void rrdcalctemplate_free(RRDHOST *host, RRDCALCTEMPLATE *rt) {
-    debug(D_HEALTH, "Health removing template '%s' of host '%s'", rt->name, host->hostname);
-
-    if(host->templates) {
-        if(host->templates == rt) {
-            host->templates = rt->next;
-        }
-        else {
-            RRDCALCTEMPLATE *t, *last = host->templates;
-            for (t = last->next; t && t != rt; last = t, t = t->next ) ;
-            if(last && last->next == rt) {
-                last->next = rt->next;
-                rt->next = NULL;
-            }
-            else
-                error("Cannot find RRDCALCTEMPLATE '%s' linked in host '%s'", rt->name, host->hostname);
-        }
-    }
-
-    expression_free(rt->calculation);
-    expression_free(rt->warning);
-    expression_free(rt->critical);
-
-    freez(rt->family_match);
-    simple_pattern_free(rt->family_pattern);
-
-    freez(rt->name);
-    freez(rt->exec);
-    freez(rt->recipient);
-    freez(rt->context);
-    freez(rt->source);
-    freez(rt->units);
-    freez(rt->info);
-    freez(rt->dimensions);
-    freez(rt);
-}
-
-// ----------------------------------------------------------------------------
-// load health configuration
-
-#define HEALTH_CONF_MAX_LINE 4096
-
-#define HEALTH_ALARM_KEY "alarm"
-#define HEALTH_TEMPLATE_KEY "template"
-#define HEALTH_ON_KEY "on"
-#define HEALTH_FAMILIES_KEY "families"
-#define HEALTH_LOOKUP_KEY "lookup"
-#define HEALTH_CALC_KEY "calc"
-#define HEALTH_EVERY_KEY "every"
-#define HEALTH_GREEN_KEY "green"
-#define HEALTH_RED_KEY "red"
-#define HEALTH_WARN_KEY "warn"
-#define HEALTH_CRIT_KEY "crit"
-#define HEALTH_EXEC_KEY "exec"
-#define HEALTH_RECIPIENT_KEY "to"
-#define HEALTH_UNITS_KEY "units"
-#define HEALTH_INFO_KEY "info"
-#define HEALTH_DELAY_KEY "delay"
-#define HEALTH_OPTIONS_KEY "options"
-
-static inline int rrdcalc_add_alarm_from_config(RRDHOST *host, RRDCALC *rc) {
-    if(!rc->chart) {
-        error("Health configuration for alarm '%s' does not have a chart", rc->name);
-        return 0;
-    }
-
-    if(!rc->update_every) {
-        error("Health configuration for alarm '%s.%s' has no frequency (parameter 'every'). Ignoring it.", rc->chart?rc->chart:"NOCHART", rc->name);
-        return 0;
-    }
-
-    if(!RRDCALC_HAS_DB_LOOKUP(rc) && !rc->warning && !rc->critical) {
-        error("Health configuration for alarm '%s.%s' is useless (no calculation, no warning and no critical evaluation)", rc->chart?rc->chart:"NOCHART", rc->name);
-        return 0;
-    }
-
-    if (rrdcalc_exists(host, rc->chart, rc->name, rc->hash_chart, rc->hash))
-        return 0;
-
-    rc->id = rrdcalc_get_unique_id(&localhost, rc->chart, rc->name, &rc->next_event_id);
-
-    debug(D_HEALTH, "Health configuration adding alarm '%s.%s' (%u): exec '%s', recipient '%s', green %Lf, red %Lf, lookup: group %d, after %d, before %d, options %u, dimensions '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s', delay up %d, delay down %d, delay max %d, delay_multiplier %f",
-          rc->chart?rc->chart:"NOCHART",
-          rc->name,
-          rc->id,
-          (rc->exec)?rc->exec:"DEFAULT",
-          (rc->recipient)?rc->recipient:"DEFAULT",
-          rc->green,
-          rc->red,
-          rc->group,
-          rc->after,
-          rc->before,
-          rc->options,
-          (rc->dimensions)?rc->dimensions:"NONE",
-          rc->update_every,
-          (rc->calculation)?rc->calculation->parsed_as:"NONE",
-          (rc->warning)?rc->warning->parsed_as:"NONE",
-          (rc->critical)?rc->critical->parsed_as:"NONE",
-          rc->source,
-          rc->delay_up_duration,
-          rc->delay_down_duration,
-          rc->delay_max_duration,
-          rc->delay_multiplier
-    );
-
-    rrdcalc_create_part2(host, rc);
-    return 1;
-}
-
-static inline int rrdcalctemplate_add_template_from_config(RRDHOST *host, RRDCALCTEMPLATE *rt) {
-    if(unlikely(!rt->context)) {
-        error("Health configuration for template '%s' does not have a context", rt->name);
-        return 0;
-    }
-
-    if(unlikely(!rt->update_every)) {
-        error("Health configuration for template '%s' has no frequency (parameter 'every'). Ignoring it.", rt->name);
-        return 0;
-    }
-
-    if(unlikely(!RRDCALCTEMPLATE_HAS_CALCULATION(rt) && !rt->warning && !rt->critical)) {
-        error("Health configuration for template '%s' is useless (no calculation, no warning and no critical evaluation)", rt->name);
-        return 0;
-    }
-
-    RRDCALCTEMPLATE *t, *last = NULL;
-    for (t = host->templates; t ; last = t, t = t->next) {
-        if(unlikely(t->hash_name == rt->hash_name && !strcmp(t->name, rt->name))) {
-            error("Health configuration template '%s' already exists for host '%s'.", rt->name, host->hostname);
-            return 0;
-        }
-    }
-
-    debug(D_HEALTH, "Health configuration adding template '%s': context '%s', exec '%s', recipient '%s', green %Lf, red %Lf, lookup: group %d, after %d, before %d, options %u, dimensions '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s', delay up %d, delay down %d, delay max %d, delay_multiplier %f",
-          rt->name,
-          (rt->context)?rt->context:"NONE",
-          (rt->exec)?rt->exec:"DEFAULT",
-          (rt->recipient)?rt->recipient:"DEFAULT",
-          rt->green,
-          rt->red,
-          rt->group,
-          rt->after,
-          rt->before,
-          rt->options,
-          (rt->dimensions)?rt->dimensions:"NONE",
-          rt->update_every,
-          (rt->calculation)?rt->calculation->parsed_as:"NONE",
-          (rt->warning)?rt->warning->parsed_as:"NONE",
-          (rt->critical)?rt->critical->parsed_as:"NONE",
-          rt->source,
-          rt->delay_up_duration,
-          rt->delay_down_duration,
-          rt->delay_max_duration,
-          rt->delay_multiplier
-    );
-
-    if(likely(last)) {
-        last->next = rt;
-    }
-    else {
-        rt->next = host->templates;
-        host->templates = rt;
-    }
-
-    return 1;
-}
-
-static inline int health_parse_duration(char *string, int *result) {
-    // make sure it is a number
-    if(!*string || !(isdigit(*string) || *string == '+' || *string == '-')) {
-        *result = 0;
-        return 0;
-    }
-
-    char *e = NULL;
-    calculated_number n = strtold(string, &e);
-    if(e && *e) {
-        switch (*e) {
-            case 'Y':
-                *result = (int) (n * 86400 * 365);
-                break;
-            case 'M':
-                *result = (int) (n * 86400 * 30);
-                break;
-            case 'w':
-                *result = (int) (n * 86400 * 7);
-                break;
-            case 'd':
-                *result = (int) (n * 86400);
-                break;
-            case 'h':
-                *result = (int) (n * 3600);
-                break;
-            case 'm':
-                *result = (int) (n * 60);
-                break;
-
-            default:
-            case 's':
-                *result = (int) (n);
-                break;
-        }
-    }
-    else
-       *result = (int)(n);
-
-    return 1;
-}
-
-static inline int health_parse_delay(
-        size_t line, const char *path, const char *file, char *string,
-        int *delay_up_duration,
-        int *delay_down_duration,
-        int *delay_max_duration,
-        float *delay_multiplier) {
-
-    char given_up = 0;
-    char given_down = 0;
-    char given_max = 0;
-    char given_multiplier = 0;
-
-    char *s = string;
-    while(*s) {
-        char *key = s;
-
-        while(*s && !isspace(*s)) s++;
-        while(*s && isspace(*s)) *s++ = '\0';
-
-        if(!*key) break;
-
-        char *value = s;
-        while(*s && !isspace(*s)) s++;
-        while(*s && isspace(*s)) *s++ = '\0';
-
-        if(!strcasecmp(key, "up")) {
-            if (!health_parse_duration(value, delay_up_duration)) {
-                error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword",
-                      line, path, file, value, key);
-            }
-            else given_up = 1;
-        }
-        else if(!strcasecmp(key, "down")) {
-            if (!health_parse_duration(value, delay_down_duration)) {
-                error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword",
-                      line, path, file, value, key);
-            }
-            else given_down = 1;
-        }
-        else if(!strcasecmp(key, "multiplier")) {
-            *delay_multiplier = strtof(value, NULL);
-            if(isnan(*delay_multiplier) || isinf(*delay_multiplier) || islessequal(*delay_multiplier, 0)) {
-                error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword",
-                      line, path, file, value, key);
-            }
-            else given_multiplier = 1;
-        }
-        else if(!strcasecmp(key, "max")) {
-            if (!health_parse_duration(value, delay_max_duration)) {
-                error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword",
-                      line, path, file, value, key);
-            }
-            else given_max = 1;
-        }
-        else {
-            error("Health configuration at line %zu of file '%s/%s': unknown keyword '%s'",
-                  line, path, file, key);
-        }
-    }
-
-    if(!given_up)
-        *delay_up_duration = 0;
-
-    if(!given_down)
-        *delay_down_duration = 0;
-
-    if(!given_multiplier)
-        *delay_multiplier = 1.0;
-
-    if(!given_max) {
-        if((*delay_max_duration) < (*delay_up_duration) * (*delay_multiplier))
-            *delay_max_duration = (*delay_up_duration) * (*delay_multiplier);
-
-        if((*delay_max_duration) < (*delay_down_duration) * (*delay_multiplier))
-            *delay_max_duration = (*delay_down_duration) * (*delay_multiplier);
-    }
-
-    return 1;
-}
-
-static inline uint32_t health_parse_options(const char *s) {
-    uint32_t options = 0;
-    char buf[100+1] = "";
-
-    while(*s) {
-        buf[0] = '\0';
-
-        // skip spaces
-        while(*s && isspace(*s))
-            s++;
-
-        // find the next space
-        size_t count = 0;
-        while(*s && count < 100 && !isspace(*s))
-            buf[count++] = *s++;
-
-        if(buf[0]) {
-            buf[count] = '\0';
-
-            if(!strcasecmp(buf, "no-clear-notification") || !strcasecmp(buf, "no-clear"))
-                options |= RRDCALC_FLAG_NO_CLEAR_NOTIFICATION;
-            else
-                error("Ignoring unknown alarm option '%s'", buf);
-        }
-    }
-
-    return options;
-}
-
-static inline int health_parse_db_lookup(
-        size_t line, const char *path, const char *file, char *string,
-        int *group_method, int *after, int *before, int *every,
-        uint32_t *options, char **dimensions
-) {
-    debug(D_HEALTH, "Health configuration parsing database lookup %zu@%s/%s: %s", line, path, file, string);
-
-    if(*dimensions) freez(*dimensions);
-    *dimensions = NULL;
-    *after = 0;
-    *before = 0;
-    *every = 0;
-    *options = 0;
-
-    char *s = string, *key;
-
-    // first is the group method
-    key = s;
-    while(*s && !isspace(*s)) s++;
-    while(*s && isspace(*s)) *s++ = '\0';
-    if(!*s) {
-        error("Health configuration invalid chart calculation at line %zu of file '%s/%s': expected group method followed by the 'after' time, but got '%s'",
-              line, path, file, key);
-        return 0;
-    }
-
-    if((*group_method = web_client_api_request_v1_data_group(key, -1)) == -1) {
-        error("Health configuration at line %zu of file '%s/%s': invalid group method '%s'",
-              line, path, file, key);
-        return 0;
-    }
-
-    // then is the 'after' time
-    key = s;
-    while(*s && !isspace(*s)) s++;
-    while(*s && isspace(*s)) *s++ = '\0';
-
-    if(!health_parse_duration(key, after)) {
-        error("Health configuration at line %zu of file '%s/%s': invalid duration '%s' after group method",
-              line, path, file, key);
-        return 0;
-    }
-
-    // sane defaults
-    *every = abs(*after);
-
-    // now we may have optional parameters
-    while(*s) {
-        key = s;
-        while(*s && !isspace(*s)) s++;
-        while(*s && isspace(*s)) *s++ = '\0';
-        if(!*key) break;
-
-        if(!strcasecmp(key, "at")) {
-            char *value = s;
-            while(*s && !isspace(*s)) s++;
-            while(*s && isspace(*s)) *s++ = '\0';
-
-            if (!health_parse_duration(value, before)) {
-                error("Health configuration at line %zu of file '%s/%s': invalid duration '%s' for '%s' keyword",
-                      line, path, file, value, key);
-            }
-        }
-        else if(!strcasecmp(key, HEALTH_EVERY_KEY)) {
-            char *value = s;
-            while(*s && !isspace(*s)) s++;
-            while(*s && isspace(*s)) *s++ = '\0';
-
-            if (!health_parse_duration(value, every)) {
-                error("Health configuration at line %zu of file '%s/%s': invalid duration '%s' for '%s' keyword",
-                      line, path, file, value, key);
-            }
-        }
-        else if(!strcasecmp(key, "absolute") || !strcasecmp(key, "abs") || !strcasecmp(key, "absolute_sum")) {
-            *options |= RRDR_OPTION_ABSOLUTE;
-        }
-        else if(!strcasecmp(key, "min2max")) {
-            *options |= RRDR_OPTION_MIN2MAX;
-        }
-        else if(!strcasecmp(key, "null2zero")) {
-            *options |= RRDR_OPTION_NULL2ZERO;
-        }
-        else if(!strcasecmp(key, "percentage")) {
-            *options |= RRDR_OPTION_PERCENTAGE;
-        }
-        else if(!strcasecmp(key, "unaligned")) {
-            *options |= RRDR_OPTION_NOT_ALIGNED;
-        }
-        else if(!strcasecmp(key, "of")) {
-            if(*s && strcasecmp(s, "all"))
-               *dimensions = strdupz(s);
-            break;
-        }
-        else {
-            error("Health configuration at line %zu of file '%s/%s': unknown keyword '%s'",
-                  line, path, file, key);
-        }
-    }
-
-    return 1;
-}
-
-static inline char *trim_all_spaces(char *buffer) {
-    char *d = buffer, *s = buffer;
-
-    // skip spaces
-    while(isspace(*s)) s++;
-
-    while(*s) {
-        // copy the non-space part
-        while(*s && !isspace(*s)) *d++ = *s++;
-
-        // add a space if we have to
-        if(*s && isspace(*s)) {
-            *d++ = ' ';
-            s++;
-        }
-
-        // skip spaces
-        while(isspace(*s)) s++;
-    }
-
-    *d = '\0';
-
-    if(d > buffer) {
-        d--;
-        if(isspace(*d)) *d = '\0';
-    }
-
-    if(!buffer[0]) return NULL;
-    return buffer;
-}
-
-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);
-    return strdupz(buffer);
-}
-
-static inline void strip_quotes(char *s) {
-    while(*s) {
-        if(*s == '\'' || *s == '"') *s = ' ';
-        s++;
-    }
-}
-
-int health_readfile(const char *path, const char *filename) {
-    debug(D_HEALTH, "Health configuration reading file '%s/%s'", path, filename);
-
-    static uint32_t
-            hash_alarm = 0,
-            hash_template = 0,
-            hash_on = 0,
-            hash_families = 0,
-            hash_calc = 0,
-            hash_green = 0,
-            hash_red = 0,
-            hash_warn = 0,
-            hash_crit = 0,
-            hash_exec = 0,
-            hash_every = 0,
-            hash_lookup = 0,
-            hash_units = 0,
-            hash_info = 0,
-            hash_recipient = 0,
-            hash_delay = 0,
-            hash_options = 0;
-
-    char buffer[HEALTH_CONF_MAX_LINE + 1];
-
-    if(unlikely(!hash_alarm)) {
-        hash_alarm = simple_uhash(HEALTH_ALARM_KEY);
-        hash_template = simple_uhash(HEALTH_TEMPLATE_KEY);
-        hash_on = simple_uhash(HEALTH_ON_KEY);
-        hash_families = simple_uhash(HEALTH_FAMILIES_KEY);
-        hash_calc = simple_uhash(HEALTH_CALC_KEY);
-        hash_lookup = simple_uhash(HEALTH_LOOKUP_KEY);
-        hash_green = simple_uhash(HEALTH_GREEN_KEY);
-        hash_red = simple_uhash(HEALTH_RED_KEY);
-        hash_warn = simple_uhash(HEALTH_WARN_KEY);
-        hash_crit = simple_uhash(HEALTH_CRIT_KEY);
-        hash_exec = simple_uhash(HEALTH_EXEC_KEY);
-        hash_every = simple_uhash(HEALTH_EVERY_KEY);
-        hash_units = simple_hash(HEALTH_UNITS_KEY);
-        hash_info = simple_hash(HEALTH_INFO_KEY);
-        hash_recipient = simple_hash(HEALTH_RECIPIENT_KEY);
-        hash_delay = simple_uhash(HEALTH_DELAY_KEY);
-        hash_options = simple_uhash(HEALTH_OPTIONS_KEY);
-    }
-
-    snprintfz(buffer, HEALTH_CONF_MAX_LINE, "%s/%s", path, filename);
-    FILE *fp = fopen(buffer, "r");
-    if(!fp) {
-        error("Health configuration cannot read file '%s'.", buffer);
-        return 0;
-    }
-
-    RRDCALC *rc = NULL;
-    RRDCALCTEMPLATE *rt = NULL;
-
-    size_t line = 0, append = 0;
-    char *s;
-    while((s = fgets(&buffer[append], (int)(HEALTH_CONF_MAX_LINE - append), fp)) || append) {
-        int stop_appending = !s;
-        line++;
-        s = trim(buffer);
-        if(!s) continue;
-
-        append = strlen(s);
-        if(!stop_appending && s[append - 1] == '\\') {
-            s[append - 1] = ' ';
-            append = &s[append] - buffer;
-            if(append < HEALTH_CONF_MAX_LINE)
-                continue;
-            else {
-                error("Health configuration has too long muli-line at line %zu of file '%s/%s'.", line, path, filename);
-            }
-        }
-        append = 0;
-
-        char *key = s;
-        while(*s && *s != ':') s++;
-        if(!*s) {
-            error("Health configuration has invalid line %zu of file '%s/%s'. It does not contain a ':'. Ignoring it.", line, path, filename);
-            continue;
-        }
-        *s = '\0';
-        s++;
-
-        char *value = s;
-        key = trim_all_spaces(key);
-        value = trim_all_spaces(value);
-
-        if(!key) {
-            error("Health configuration has invalid line %zu of file '%s/%s'. Keyword is empty. Ignoring it.", line, path, filename);
-            continue;
-        }
-
-        if(!value) {
-            error("Health configuration has invalid line %zu of file '%s/%s'. value is empty. Ignoring it.", line, path, filename);
-            continue;
-        }
-
-        uint32_t hash = simple_uhash(key);
-
-        if(hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) {
-            if(rc && !rrdcalc_add_alarm_from_config(&localhost, rc))
-                rrdcalc_free(&localhost, rc);
-
-            if(rt) {
-                if (!rrdcalctemplate_add_template_from_config(&localhost, rt))
-                    rrdcalctemplate_free(&localhost, rt);
-                rt = NULL;
-            }
-
-            rc = callocz(1, sizeof(RRDCALC));
-            rc->next_event_id = 1;
-            rc->name = strdupz(value);
-            rc->hash = simple_hash(rc->name);
-            rc->source = health_source_file(line, path, filename);
-            rc->green = NAN;
-            rc->red = NAN;
-            rc->value = NAN;
-            rc->old_value = NAN;
-            rc->delay_multiplier = 1.0;
-
-            if(rrdvar_fix_name(rc->name))
-                error("Health configuration renamed alarm '%s' to '%s'", value, rc->name);
-        }
-        else if(hash == hash_template && !strcasecmp(key, HEALTH_TEMPLATE_KEY)) {
-            if(rc) {
-                if(!rrdcalc_add_alarm_from_config(&localhost, rc))
-                    rrdcalc_free(&localhost, rc);
-                rc = NULL;
-            }
-
-            if(rt && !rrdcalctemplate_add_template_from_config(&localhost, rt))
-                rrdcalctemplate_free(&localhost, rt);
-
-            rt = callocz(1, sizeof(RRDCALCTEMPLATE));
-            rt->name = strdupz(value);
-            rt->hash_name = simple_hash(rt->name);
-            rt->source = health_source_file(line, path, filename);
-            rt->green = NAN;
-            rt->red = NAN;
-            rt->delay_multiplier = 1.0;
-
-            if(rrdvar_fix_name(rt->name))
-                error("Health configuration renamed template '%s' to '%s'", value, rt->name);
-        }
-        else if(rc) {
-            if(hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) {
-                if(rc->chart) {
-                    if(strcmp(rc->chart, value))
-                        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->hash_chart = simple_hash(rc->chart);
-            }
-            else if(hash == hash_lookup && !strcasecmp(key, HEALTH_LOOKUP_KEY)) {
-                health_parse_db_lookup(line, path, filename, value, &rc->group, &rc->after, &rc->before,
-                                       &rc->update_every,
-                                       &rc->options, &rc->dimensions);
-            }
-            else if(hash == hash_every && !strcasecmp(key, HEALTH_EVERY_KEY)) {
-                if(!health_parse_duration(value, &rc->update_every))
-                    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) {
-                    error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.",
-                         line, path, filename, rc->name, key, e);
-                }
-            }
-            else if(hash == hash_red && !strcasecmp(key, HEALTH_RED_KEY)) {
-                char *e;
-                rc->red = strtold(value, &e);
-                if(e && *e) {
-                    error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.",
-                         line, path, filename, rc->name, key, e);
-                }
-            }
-            else if(hash == hash_calc && !strcasecmp(key, HEALTH_CALC_KEY)) {
-                const char *failed_at = NULL;
-                int error = 0;
-                rc->calculation = expression_parse(value, &failed_at, &error);
-                if(!rc->calculation) {
-                    error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
-                          line, path, filename, rc->name, key, value, expression_strerror(error), failed_at);
-                }
-            }
-            else if(hash == hash_warn && !strcasecmp(key, HEALTH_WARN_KEY)) {
-                const char *failed_at = NULL;
-                int error = 0;
-                rc->warning = expression_parse(value, &failed_at, &error);
-                if(!rc->warning) {
-                    error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
-                          line, path, filename, rc->name, key, value, expression_strerror(error), failed_at);
-                }
-            }
-            else if(hash == hash_crit && !strcasecmp(key, HEALTH_CRIT_KEY)) {
-                const char *failed_at = NULL;
-                int error = 0;
-                rc->critical = expression_parse(value, &failed_at, &error);
-                if(!rc->critical) {
-                    error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
-                          line, path, filename, rc->name, key, value, expression_strerror(error), failed_at);
-                }
-            }
-            else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) {
-                if(rc->exec) {
-                    if(strcmp(rc->exec, value))
-                        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);
-            }
-            else if(hash == hash_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) {
-                if(rc->recipient) {
-                    if(strcmp(rc->recipient, value))
-                        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);
-            }
-            else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) {
-                if(rc->units) {
-                    if(strcmp(rc->units, value))
-                        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);
-                strip_quotes(rc->units);
-            }
-            else if(hash == hash_info && !strcasecmp(key, HEALTH_INFO_KEY)) {
-                if(rc->info) {
-                    if(strcmp(rc->info, value))
-                        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);
-                strip_quotes(rc->info);
-            }
-            else if(hash == hash_delay && !strcasecmp(key, HEALTH_DELAY_KEY)) {
-                health_parse_delay(line, path, filename, value, &rc->delay_up_duration, &rc->delay_down_duration, &rc->delay_max_duration, &rc->delay_multiplier);
-            }
-            else if(hash == hash_options && !strcasecmp(key, HEALTH_OPTIONS_KEY)) {
-                rc->options |= health_parse_options(value);
-            }
-            else {
-                error("Health configuration at line %zu of file '%s/%s' for alarm '%s' has unknown key '%s'.",
-                     line, path, filename, rc->name, key);
-            }
-        }
-        else if(rt) {
-            if(hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) {
-                if(rt->context) {
-                    if(strcmp(rt->context, value))
-                        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->hash_context = simple_hash(rt->context);
-            }
-            else if(hash == hash_families && !strcasecmp(key, HEALTH_FAMILIES_KEY)) {
-                freez(rt->family_match);
-                simple_pattern_free(rt->family_pattern);
-
-                rt->family_match = strdupz(value);
-                rt->family_pattern = simple_pattern_create(rt->family_match, SIMPLE_PATTERN_EXACT);
-            }
-            else if(hash == hash_lookup && !strcasecmp(key, HEALTH_LOOKUP_KEY)) {
-                health_parse_db_lookup(line, path, filename, value, &rt->group, &rt->after, &rt->before,
-                                       &rt->update_every, &rt->options, &rt->dimensions);
-            }
-            else if(hash == hash_every && !strcasecmp(key, HEALTH_EVERY_KEY)) {
-                if(!health_parse_duration(value, &rt->update_every))
-                    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) {
-                    error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.",
-                         line, path, filename, rt->name, key, e);
-                }
-            }
-            else if(hash == hash_red && !strcasecmp(key, HEALTH_RED_KEY)) {
-                char *e;
-                rt->red = strtold(value, &e);
-                if(e && *e) {
-                    error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.",
-                         line, path, filename, rt->name, key, e);
-                }
-            }
-            else if(hash == hash_calc && !strcasecmp(key, HEALTH_CALC_KEY)) {
-                const char *failed_at = NULL;
-                int error = 0;
-                rt->calculation = expression_parse(value, &failed_at, &error);
-                if(!rt->calculation) {
-                    error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
-                          line, path, filename, rt->name, key, value, expression_strerror(error), failed_at);
-                }
-            }
-            else if(hash == hash_warn && !strcasecmp(key, HEALTH_WARN_KEY)) {
-                const char *failed_at = NULL;
-                int error = 0;
-                rt->warning = expression_parse(value, &failed_at, &error);
-                if(!rt->warning) {
-                    error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
-                          line, path, filename, rt->name, key, value, expression_strerror(error), failed_at);
-                }
-            }
-            else if(hash == hash_crit && !strcasecmp(key, HEALTH_CRIT_KEY)) {
-                const char *failed_at = NULL;
-                int error = 0;
-                rt->critical = expression_parse(value, &failed_at, &error);
-                if(!rt->critical) {
-                    error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
-                          line, path, filename, rt->name, key, value, expression_strerror(error), failed_at);
-                }
-            }
-            else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) {
-                if(rt->exec) {
-                    if(strcmp(rt->exec, value))
-                        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);
-            }
-            else if(hash == hash_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) {
-                if(rt->recipient) {
-                    if(strcmp(rt->recipient, value))
-                        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);
-            }
-            else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) {
-                if(rt->units) {
-                    if(strcmp(rt->units, value))
-                        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);
-                strip_quotes(rt->units);
-            }
-            else if(hash == hash_info && !strcasecmp(key, HEALTH_INFO_KEY)) {
-                if(rt->info) {
-                    if(strcmp(rt->info, value))
-                        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);
-                strip_quotes(rt->info);
-            }
-            else if(hash == hash_delay && !strcasecmp(key, HEALTH_DELAY_KEY)) {
-                health_parse_delay(line, path, filename, value, &rt->delay_up_duration, &rt->delay_down_duration, &rt->delay_max_duration, &rt->delay_multiplier);
-            }
-            else if(hash == hash_options && !strcasecmp(key, HEALTH_OPTIONS_KEY)) {
-                rt->options |= health_parse_options(value);
-            }
-            else {
-                error("Health configuration at line %zu of file '%s/%s' for template '%s' has unknown key '%s'.",
-                      line, path, filename, rt->name, key);
-            }
-        }
-        else {
-            error("Health configuration at line %zu of file '%s/%s' has unknown key '%s'. Expected either '" HEALTH_ALARM_KEY "' or '" HEALTH_TEMPLATE_KEY "'.",
-                  line, path, filename, key);
-        }
-    }
-
-    if(rc && !rrdcalc_add_alarm_from_config(&localhost, rc))
-        rrdcalc_free(&localhost, rc);
-
-    if(rt && !rrdcalctemplate_add_template_from_config(&localhost, rt))
-        rrdcalctemplate_free(&localhost, rt);
-
-    fclose(fp);
-    return 1;
-}
-
-void health_readdir(const char *path) {
-    size_t pathlen = strlen(path);
-
-    debug(D_HEALTH, "Health configuration reading directory '%s'", path);
-
-    DIR *dir = opendir(path);
-    if (!dir) {
-        error("Health configuration cannot open directory '%s'.", path);
-        return;
-    }
-
-    struct dirent *de = NULL;
-    while ((de = readdir(dir))) {
-        size_t len = strlen(de->d_name);
-
-        if(de->d_type == DT_DIR
-           && (
-                   (de->d_name[0] == '.' && de->d_name[1] == '\0')
-                   || (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
-           )) {
-            debug(D_HEALTH, "Ignoring directory '%s'", de->d_name);
-            continue;
-        }
-
-        else if(de->d_type == DT_DIR) {
-            char *s = mallocz(pathlen + strlen(de->d_name) + 2);
-            strcpy(s, path);
-            strcat(s, "/");
-            strcat(s, de->d_name);
-            health_readdir(s);
-            freez(s);
-            continue;
-        }
-
-        else if((de->d_type == DT_LNK || de->d_type == DT_REG || de->d_type == DT_UNKNOWN) &&
-                len > 5 && !strcmp(&de->d_name[len - 5], ".conf")) {
-            health_readfile(path, de->d_name);
-        }
-
-        else debug(D_HEALTH, "Ignoring file '%s'", de->d_name);
-    }
-
-    closedir(dir);
-}
+// ----------------------------------------------------------------------------
+// health initialization
 
-static inline char *health_config_dir(void) {
+inline char *health_config_dir(void) {
     char buffer[FILENAME_MAX + 1];
     snprintfz(buffer, FILENAME_MAX, "%s/health.d", netdata_configured_config_dir);
-    return config_get("health", "health configuration directory", buffer);
+    return config_get(CONFIG_SECTION_HEALTH, "health configuration directory", buffer);
 }
 
 void health_init(void) {
     debug(D_HEALTH, "Health configuration initializing");
 
-    if(!(health_enabled = config_get_boolean("health", "enabled", 1))) {
+    if(!(default_health_enabled = config_get_boolean(CONFIG_SECTION_HEALTH, "enabled", 1))) {
         debug(D_HEALTH, "Health is disabled.");
         return;
     }
-
-    char pathname[FILENAME_MAX + 1];
-    snprintfz(pathname, FILENAME_MAX, "%s/health", netdata_configured_varlib_dir);
-    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();
-
-    snprintfz(filename, FILENAME_MAX, "%s/alarm-notify.sh", netdata_configured_plugins_dir);
-    health.health_default_exec = config_get("health", "script to execute on alarm", filename);
-
-    long n = config_get_number("health", "in memory max health log entries", (long)localhost.health_log.max);
-    if(n < 10) {
-        error("Health configuration has invalid max log entries %ld. Using default %u", n, localhost.health_log.max);
-        config_set_number("health", "in memory max health log entries", (long)localhost.health_log.max);
-    }
-    else localhost.health_log.max = (unsigned int)n;
-
-    rrdhost_rwlock(&localhost);
-    health_readdir(path);
-    rrdhost_unlock(&localhost);
 }
 
 // ----------------------------------------------------------------------------
-// JSON generation
-
-static inline void health_string2json(BUFFER *wb, const char *prefix, const char *label, const char *value, const char *suffix) {
-    if(value && *value)
-        buffer_sprintf(wb, "%s\"%s\":\"%s\"%s", prefix, label, value, suffix);
-    else
-        buffer_sprintf(wb, "%s\"%s\":null%s", prefix, label, suffix);
-}
-
-static inline void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae, RRDHOST *host) {
-    buffer_sprintf(wb,
-            "\n\t{\n"
-                    "\t\t\"hostname\": \"%s\",\n"
-                    "\t\t\"unique_id\": %u,\n"
-                    "\t\t\"alarm_id\": %u,\n"
-                    "\t\t\"alarm_event_id\": %u,\n"
-                    "\t\t\"name\": \"%s\",\n"
-                    "\t\t\"chart\": \"%s\",\n"
-                    "\t\t\"family\": \"%s\",\n"
-                    "\t\t\"processed\": %s,\n"
-                    "\t\t\"updated\": %s,\n"
-                    "\t\t\"exec_run\": %lu,\n"
-                    "\t\t\"exec_failed\": %s,\n"
-                    "\t\t\"exec\": \"%s\",\n"
-                    "\t\t\"recipient\": \"%s\",\n"
-                    "\t\t\"exec_code\": %d,\n"
-                    "\t\t\"source\": \"%s\",\n"
-                    "\t\t\"units\": \"%s\",\n"
-                    "\t\t\"info\": \"%s\",\n"
-                    "\t\t\"when\": %lu,\n"
-                    "\t\t\"duration\": %lu,\n"
-                    "\t\t\"non_clear_duration\": %lu,\n"
-                    "\t\t\"status\": \"%s\",\n"
-                    "\t\t\"old_status\": \"%s\",\n"
-                    "\t\t\"delay\": %d,\n"
-                    "\t\t\"delay_up_to_timestamp\": %lu,\n"
-                    "\t\t\"updated_by_id\": %u,\n"
-                    "\t\t\"updates_id\": %u,\n"
-                    "\t\t\"value_string\": \"%s\",\n"
-                    "\t\t\"old_value_string\": \"%s\",\n"
-            , host->hostname
-            , ae->unique_id
-            , ae->alarm_id
-            , ae->alarm_event_id
-            , ae->name
-            , ae->chart
-            , ae->family
-            , (ae->flags & HEALTH_ENTRY_FLAG_PROCESSED)?"true":"false"
-            , (ae->flags & HEALTH_ENTRY_FLAG_UPDATED)?"true":"false"
-            , (unsigned long)ae->exec_run_timestamp
-            , (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
-            , ae->source
-            , ae->units?ae->units:""
-            , ae->info?ae->info:""
-            , (unsigned long)ae->when
-            , (unsigned long)ae->duration
-            , (unsigned long)ae->non_clear_duration
-            , rrdcalc_status2string(ae->new_status)
-            , rrdcalc_status2string(ae->old_status)
-            , ae->delay
-            , (unsigned long)ae->delay_up_to_timestamp
-            , ae->updated_by_id
-            , ae->updates_id
-            , ae->new_value_string
-            , ae->old_value_string
-    );
-
-    if(unlikely(ae->flags & HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION)) {
-        buffer_strcat(wb, "\t\t\"no_clear_notification\": true,\n");
-    }
-
-    buffer_strcat(wb, "\t\t\"value\":");
-    buffer_rrd_value(wb, ae->new_value);
-    buffer_strcat(wb, ",\n");
-
-    buffer_strcat(wb, "\t\t\"old_value\":");
-    buffer_rrd_value(wb, ae->old_value);
-    buffer_strcat(wb, "\n");
-
-    buffer_strcat(wb, "\t}");
-}
-
-void health_alarm_log2json(RRDHOST *host, BUFFER *wb, uint32_t after) {
-    pthread_rwlock_rdlock(&host->health_log.alarm_log_rwlock);
-
-    buffer_strcat(wb, "[");
-
-    unsigned int max = host->health_log.max;
-    unsigned int count = 0;
-    ALARM_ENTRY *ae;
-    for(ae = host->health_log.alarms; ae && count < max ; count++, ae = ae->next) {
-        if(ae->unique_id > after) {
-            if(likely(count)) buffer_strcat(wb, ",");
-            health_alarm_entry2json_nolock(wb, ae, host);
-        }
-    }
-
-    buffer_strcat(wb, "\n]\n");
-
-    pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock);
-}
-
-static inline void health_rrdcalc2json_nolock(BUFFER *wb, RRDCALC *rc) {
-    char value_string[100 + 1];
-    format_value_and_unit(value_string, 100, rc->value, rc->units, -1);
-
-    buffer_sprintf(wb,
-           "\t\t\"%s.%s\": {\n"
-                   "\t\t\t\"id\": %lu,\n"
-                   "\t\t\t\"name\": \"%s\",\n"
-                   "\t\t\t\"chart\": \"%s\",\n"
-                   "\t\t\t\"family\": \"%s\",\n"
-                   "\t\t\t\"active\": %s,\n"
-                   "\t\t\t\"exec\": \"%s\",\n"
-                   "\t\t\t\"recipient\": \"%s\",\n"
-                   "\t\t\t\"source\": \"%s\",\n"
-                   "\t\t\t\"units\": \"%s\",\n"
-                   "\t\t\t\"info\": \"%s\",\n"
-                                  "\t\t\t\"status\": \"%s\",\n"
-                   "\t\t\t\"last_status_change\": %lu,\n"
-                   "\t\t\t\"last_updated\": %lu,\n"
-                   "\t\t\t\"next_update\": %lu,\n"
-                   "\t\t\t\"update_every\": %d,\n"
-                   "\t\t\t\"delay_up_duration\": %d,\n"
-                   "\t\t\t\"delay_down_duration\": %d,\n"
-                   "\t\t\t\"delay_max_duration\": %d,\n"
-                   "\t\t\t\"delay_multiplier\": %f,\n"
-                   "\t\t\t\"delay\": %d,\n"
-                   "\t\t\t\"delay_up_to_timestamp\": %lu,\n"
-                   "\t\t\t\"value_string\": \"%s\",\n"
-           , rc->chart, rc->name
-           , (unsigned long)rc->id
-           , rc->name
-           , rc->chart
-           , (rc->rrdset && rc->rrdset->family)?rc->rrdset->family:""
-           , (rc->rrdset)?"true":"false"
-           , rc->exec?rc->exec:health.health_default_exec
-           , rc->recipient?rc->recipient:health.health_default_recipient
-           , rc->source
-           , rc->units?rc->units:""
-           , rc->info?rc->info:""
-           , rrdcalc_status2string(rc->status)
-           , (unsigned long)rc->last_status_change
-           , (unsigned long)rc->last_updated
-           , (unsigned long)rc->next_update
-           , rc->update_every
-           , rc->delay_up_duration
-           , rc->delay_down_duration
-           , rc->delay_max_duration
-           , rc->delay_multiplier
-           , rc->delay_last
-           , (unsigned long)rc->delay_up_to_timestamp
-           , value_string
-    );
-
-    if(unlikely(rc->options & RRDCALC_FLAG_NO_CLEAR_NOTIFICATION)) {
-        buffer_strcat(wb, "\t\t\t\"no_clear_notification\": true,\n");
-    }
-
-    if(RRDCALC_HAS_DB_LOOKUP(rc)) {
-        if(rc->dimensions && *rc->dimensions)
-            health_string2json(wb, "\t\t\t", "lookup_dimensions", rc->dimensions, ",\n");
-
-        buffer_sprintf(wb,
-                       "\t\t\t\"db_after\": %lu,\n"
-                       "\t\t\t\"db_before\": %lu,\n"
-                       "\t\t\t\"lookup_method\": \"%s\",\n"
-                       "\t\t\t\"lookup_after\": %d,\n"
-                       "\t\t\t\"lookup_before\": %d,\n"
-                       "\t\t\t\"lookup_options\": \"",
-                       (unsigned long) rc->db_after,
-                       (unsigned long) rc->db_before,
-                       group_method2string(rc->group),
-                       rc->after,
-                       rc->before
-        );
-        buffer_data_options2string(wb, rc->options);
-        buffer_strcat(wb, "\",\n");
-    }
-
-    if(rc->calculation) {
-        health_string2json(wb, "\t\t\t", "calc", rc->calculation->source, ",\n");
-        health_string2json(wb, "\t\t\t", "calc_parsed", rc->calculation->parsed_as, ",\n");
-    }
-
-    if(rc->warning) {
-        health_string2json(wb, "\t\t\t", "warn", rc->warning->source, ",\n");
-        health_string2json(wb, "\t\t\t", "warn_parsed", rc->warning->parsed_as, ",\n");
-    }
-
-    if(rc->critical) {
-        health_string2json(wb, "\t\t\t", "crit", rc->critical->source, ",\n");
-        health_string2json(wb, "\t\t\t", "crit_parsed", rc->critical->parsed_as, ",\n");
-    }
-
-    buffer_strcat(wb, "\t\t\t\"green\":");
-    buffer_rrd_value(wb, rc->green);
-    buffer_strcat(wb, ",\n");
-
-    buffer_strcat(wb, "\t\t\t\"red\":");
-    buffer_rrd_value(wb, rc->red);
-    buffer_strcat(wb, ",\n");
-
-    buffer_strcat(wb, "\t\t\t\"value\":");
-    buffer_rrd_value(wb, rc->value);
-    buffer_strcat(wb, "\n");
-
-    buffer_strcat(wb, "\t\t}");
-}
-
-//void health_rrdcalctemplate2json_nolock(BUFFER *wb, RRDCALCTEMPLATE *rt) {
-//
-//}
-
-void health_alarms2json(RRDHOST *host, BUFFER *wb, int all) {
-    int i;
-
-    rrdhost_rdlock(&localhost);
-    buffer_sprintf(wb, "{\n\t\"hostname\": \"%s\","
-                        "\n\t\"latest_alarm_log_unique_id\": %u,"
-                        "\n\t\"status\": %s,"
-                        "\n\t\"now\": %lu,"
-                        "\n\t\"alarms\": {\n",
-                        host->hostname,
-                        (host->health_log.next_log_id > 0)?(host->health_log.next_log_id - 1):0,
-                        health_enabled?"true":"false",
-                        (unsigned long)now_realtime_sec());
-
-    RRDCALC *rc;
-    for(i = 0, rc = host->alarms; rc ; rc = rc->next) {
-        if(unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec))
-            continue;
-
-        if(likely(!all && !(rc->status == RRDCALC_STATUS_WARNING || rc->status == RRDCALC_STATUS_CRITICAL)))
-            continue;
-
-        if(likely(i)) buffer_strcat(wb, ",\n");
-        health_rrdcalc2json_nolock(wb, rc);
-        i++;
-    }
-
-//    buffer_strcat(wb, "\n\t},\n\t\"templates\": {");
-//    RRDCALCTEMPLATE *rt;
-//    for(rt = host->templates; rt ; rt = rt->next)
-//        health_rrdcalctemplate2json_nolock(wb, rt);
+// re-load health configuration
 
-    buffer_strcat(wb, "\n\t}\n}\n");
-    rrdhost_unlock(&localhost);
-}
+void health_reload_host(RRDHOST *host) {
+    if(unlikely(!host->health_enabled))
+        return;
 
+    char *path = health_config_dir();
 
-// ----------------------------------------------------------------------------
-// re-load health configuration
+    // free all running alarms
+    rrdhost_wrlock(host);
 
-static inline void health_free_all_nolock(RRDHOST *host) {
     while(host->templates)
         rrdcalctemplate_free(host, host->templates);
 
     while(host->alarms)
         rrdcalc_free(host, host->alarms);
-}
 
-void health_reload(void) {
-    if(!health_enabled) {
-        error("Health reload is requested, but health is not enabled.");
-        return;
-    }
-
-    char *path = health_config_dir();
-
-    // free all running alarms
-    rrdhost_rwlock(&localhost);
-    health_free_all_nolock(&localhost);
-    rrdhost_unlock(&localhost);
+    rrdhost_unlock(host);
 
     // invalidate all previous entries in the alarm log
     ALARM_ENTRY *t;
-    for(t = localhost.health_log.alarms ; t ; t = t->next) {
+    for(t = host->health_log.alarms ; t ; t = t->next) {
         if(t->new_status != RRDCALC_STATUS_REMOVED)
             t->flags |= HEALTH_ENTRY_FLAG_UPDATED;
     }
 
+    rrdhost_rdlock(host);
     // reset all thresholds to all charts
     RRDSET *st;
-    for(st = localhost.rrdset_root; st ; st = st->next) {
+    rrdset_foreach_read(st, host) {
         st->green = NAN;
         st->red = NAN;
     }
+    rrdhost_unlock(host);
 
     // load the new alarms
-    rrdhost_rwlock(&localhost);
-    health_readdir(path);
-    rrdhost_unlock(&localhost);
+    rrdhost_wrlock(host);
+    health_readdir(host, path);
 
     // link the loaded alarms to their charts
-    for(st = localhost.rrdset_root; st ; st = st->next) {
-        rrdhost_rwlock(&localhost);
-
+    rrdset_foreach_write(st, host) {
         rrdsetcalc_link_matching(st);
         rrdcalctemplate_link_matching(st);
-
-        rrdhost_unlock(&localhost);
     }
+
+    rrdhost_unlock(host);
+}
+
+void health_reload(void) {
+
+    rrd_rdlock();
+
+    RRDHOST *host;
+    rrdhost_foreach_read(host)
+        health_reload_host(host);
+
+    rrd_unlock();
 }
 
 // ----------------------------------------------------------------------------
@@ -2800,11 +142,8 @@ static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) {
     static char command_to_run[ALARM_EXEC_COMMAND_LENGTH + 1];
     pid_t command_pid;
 
-    const char *exec = ae->exec;
-    if(!exec) exec = health.health_default_exec;
-
-    const char *recipient = ae->recipient;
-    if(!recipient) recipient = health.health_default_recipient;
+    const char *exec      = (ae->exec)      ? ae->exec      : host->health_default_exec;
+    const char *recipient = (ae->recipient) ? ae->recipient : host->health_default_recipient;
 
     snprintfz(command_to_run, ALARM_EXEC_COMMAND_LENGTH, "exec %s '%s' '%s' '%u' '%u' '%u' '%lu' '%s' '%s' '%s' '%s' '%s' '%0.0Lf' '%0.0Lf' '%s' '%u' '%u' '%s' '%s' '%s' '%s'",
               exec,
@@ -2911,17 +250,7 @@ static inline void health_alarm_log_process(RRDHOST *host) {
 
         ALARM_ENTRY *t = ae->next;
 
-        freez(ae->name);
-        freez(ae->chart);
-        freez(ae->family);
-        freez(ae->exec);
-        freez(ae->recipient);
-        freez(ae->source);
-        freez(ae->units);
-        freez(ae->info);
-        freez(ae->old_value_string);
-        freez(ae->new_value_string);
-        freez(ae);
+        health_alarm_log_free_one_nochecks_nounlink(ae);
 
         ae = t;
         host->health_log.count--;
@@ -2995,319 +324,396 @@ void *health_main(void *ptr) {
     if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
         error("Cannot set pthread cancel state to ENABLE.");
 
-    int min_run_every = (int)config_get_number("health", "run at least every seconds", 10);
+    int min_run_every = (int)config_get_number(CONFIG_SECTION_HEALTH, "run at least every seconds", 10);
     if(min_run_every < 1) min_run_every = 1;
 
     BUFFER *wb = buffer_create(100);
 
+    time_t now               = now_realtime_sec();
+    time_t now_boottime      = now_boottime_sec();
+    time_t last_now          = now;
+    time_t last_now_boottime = now_boottime;
+    time_t hibernation_delay = config_get_number(CONFIG_SECTION_HEALTH, "postpone alarms during hibernation for seconds", 60);
+
     unsigned int loop = 0;
-    while(health_enabled && !netdata_exit) {
+    while(!netdata_exit) {
         loop++;
         debug(D_HEALTH, "Health monitoring iteration no %u started", loop);
 
-        int oldstate, runnable = 0;
-        time_t now = now_realtime_sec();
+        int oldstate, runnable = 0, apply_hibernation_delay = 0;
         time_t next_run = now + min_run_every;
         RRDCALC *rc;
 
+        // detect if boottime and realtime have twice the difference
+        // in which case we assume the system was just waken from hibernation
+        if(unlikely(now - last_now > 2 * (now_boottime - last_now_boottime)))
+            apply_hibernation_delay = 1;
+
+        last_now = now;
+        last_now_boottime = now_boottime;
+
         if(unlikely(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate) != 0))
             error("Cannot set pthread cancel state to DISABLE.");
 
-        rrdhost_rdlock(&localhost);
+        rrd_rdlock();
 
-        // the first loop is to lookup values from the db
-        for(rc = localhost.alarms; rc; rc = rc->next) {
-            if(unlikely(!rrdcalc_isrunnable(rc, now, &next_run))) {
-                if(unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_RUNNABLE))
-                    rc->rrdcalc_flags &= ~RRDCALC_FLAG_RUNNABLE;
+        RRDHOST *host;
+        rrdhost_foreach_read(host) {
+            if(unlikely(!host->health_enabled))
                 continue;
-            }
 
-            runnable++;
-            rc->old_value = rc->value;
-            rc->rrdcalc_flags |= RRDCALC_FLAG_RUNNABLE;
+            if(unlikely(apply_hibernation_delay)) {
 
-            // 1. if there is database lookup, do it
-            // 2. if there is calculation expression, run it
+                info("Postponing alarm checks for %ld seconds, on host '%s', due to boottime discrepancy (realtime dt: %ld, boottime dt: %ld)."
+                     , hibernation_delay
+                     , host->hostname
+                     , (long)(now - last_now)
+                     , (long)(now_boottime - last_now_boottime)
+                );
 
-            if (unlikely(RRDCALC_HAS_DB_LOOKUP(rc))) {
-                /* time_t old_db_timestamp = rc->db_before; */
-                int value_is_null = 0;
-
-                int ret = rrd2value(rc->rrdset, wb, &rc->value,
-                                    rc->dimensions, 1, rc->after, rc->before, rc->group,
-                                    rc->options, &rc->db_after, &rc->db_before, &value_is_null);
+                host->health_delay_up_to = now + hibernation_delay;
+            }
 
-                if (unlikely(ret != 200)) {
-                    // database lookup failed
-                    rc->value = NAN;
+            if(unlikely(!host->health_enabled || now < host->health_delay_up_to))
+                continue;
 
-                    debug(D_HEALTH, "Health alarm '%s.%s': database lookup returned error %d", rc->chart?rc->chart:"NOCHART", rc->name, ret);
+            rrdhost_rdlock(host);
 
-                    if (unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_DB_ERROR))) {
-                        rc->rrdcalc_flags |= RRDCALC_FLAG_DB_ERROR;
-                        error("Health alarm '%s.%s': database lookup returned error %d", rc->chart?rc->chart:"NOCHART", rc->name, ret);
-                    }
+            // the first loop is to lookup values from the db
+            for(rc = host->alarms; rc; rc = rc->next) {
+                if(unlikely(!rrdcalc_isrunnable(rc, now, &next_run))) {
+                    if(unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_RUNNABLE))
+                        rc->rrdcalc_flags &= ~RRDCALC_FLAG_RUNNABLE;
+                    continue;
                 }
-                else if (unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_DB_ERROR))
-                    rc->rrdcalc_flags &= ~RRDCALC_FLAG_DB_ERROR;
 
-                /* - RRDCALC_FLAG_DB_STALE not currently used
-                if (unlikely(old_db_timestamp == rc->db_before)) {
-                    // database is stale
+                runnable++;
+                rc->old_value = rc->value;
+                rc->rrdcalc_flags |= RRDCALC_FLAG_RUNNABLE;
+
+                // ------------------------------------------------------------
+                // if there is database lookup, do it
+
+                if(unlikely(RRDCALC_HAS_DB_LOOKUP(rc))) {
+                    /* time_t old_db_timestamp = rc->db_before; */
+                    int value_is_null = 0;
+
+                    int ret = rrd2value(rc->rrdset
+                                        , wb
+                                        , &rc->value
+                                        , rc->dimensions
+                                        , 1
+                                        , rc->after
+                                        , rc->before
+                                        , rc->group
+                                        , rc->options
+                                        , &rc->db_after
+                                        , &rc->db_before
+                                        , &value_is_null
+                    );
 
-                    debug(D_HEALTH, "Health alarm '%s.%s': database is stale", rc->chart?rc->chart:"NOCHART", rc->name);
+                    if(unlikely(ret != 200)) {
+                        // database lookup failed
+                        rc->value = NAN;
+                        rc->rrdcalc_flags |= RRDCALC_FLAG_DB_ERROR;
 
-                    if (unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_DB_STALE))) {
-                        rc->rrdcalc_flags |= RRDCALC_FLAG_DB_STALE;
-                        error("Health alarm '%s.%s': database is stale", rc->chart?rc->chart:"NOCHART", rc->name);
+                        debug(D_HEALTH
+                              , "Health on host '%s', alarm '%s.%s': database lookup returned error %d"
+                              , host->hostname
+                              , rc->chart ? rc->chart : "NOCHART"
+                              , rc->name
+                              , ret
+                        );
                     }
-                }
-                else if (unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_DB_STALE))
-                    rc->rrdcalc_flags &= ~RRDCALC_FLAG_DB_STALE;
-                */
+                    else
+                        rc->rrdcalc_flags &= ~RRDCALC_FLAG_DB_ERROR;
 
-                if (unlikely(value_is_null)) {
-                    // collected value is null
+                    /* - RRDCALC_FLAG_DB_STALE not currently used
+                    if (unlikely(old_db_timestamp == rc->db_before)) {
+                        // database is stale
 
-                    rc->value = NAN;
+                        debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': database is stale", host->hostname, rc->chart?rc->chart:"NOCHART", rc->name);
 
-                    debug(D_HEALTH, "Health alarm '%s.%s': database lookup returned empty value (possibly value is not collected yet)",
-                          rc->chart?rc->chart:"NOCHART", rc->name);
+                        if (unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_DB_STALE))) {
+                            rc->rrdcalc_flags |= RRDCALC_FLAG_DB_STALE;
+                            error("Health on host '%s', alarm '%s.%s': database is stale", host->hostname, rc->chart?rc->chart:"NOCHART", rc->name);
+                        }
+                    }
+                    else if (unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_DB_STALE))
+                        rc->rrdcalc_flags &= ~RRDCALC_FLAG_DB_STALE;
+                    */
 
-                    if (unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_DB_NAN))) {
+                    if(unlikely(value_is_null)) {
+                        // collected value is null
+                        rc->value = NAN;
                         rc->rrdcalc_flags |= RRDCALC_FLAG_DB_NAN;
-                        error("Health alarm '%s.%s': database lookup returned empty value (possibly value is not collected yet)",
-                              rc->chart?rc->chart:"NOCHART", rc->name);
+
+                        debug(D_HEALTH
+                              , "Health on host '%s', alarm '%s.%s': database lookup returned empty value (possibly value is not collected yet)"
+                              , host->hostname
+                              , rc->chart ? rc->chart : "NOCHART"
+                              , rc->name
+                        );
                     }
+                    else
+                        rc->rrdcalc_flags &= ~RRDCALC_FLAG_DB_NAN;
+
+                    debug(D_HEALTH
+                          , "Health on host '%s', alarm '%s.%s': database lookup gave value " CALCULATED_NUMBER_FORMAT
+                          , host->hostname
+                          , rc->chart ? rc->chart : "NOCHART"
+                          , rc->name
+                          , rc->value
+                    );
                 }
-                else if (unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_DB_NAN))
-                    rc->rrdcalc_flags &= ~RRDCALC_FLAG_DB_NAN;
-
-                debug(D_HEALTH, "Health alarm '%s.%s': database lookup gave value "
-                        CALCULATED_NUMBER_FORMAT, rc->chart?rc->chart:"NOCHART", rc->name, rc->value);
-            }
-
-            if(unlikely(rc->calculation)) {
-                if (unlikely(!expression_evaluate(rc->calculation))) {
-                    // calculation failed
 
-                    rc->value = NAN;
+                // ------------------------------------------------------------
+                // if there is calculation expression, run it
 
-                    debug(D_HEALTH, "Health alarm '%s.%s': expression '%s' failed: %s",
-                          rc->chart?rc->chart:"NOCHART", rc->name, rc->calculation->parsed_as, buffer_tostring(rc->calculation->error_msg));
-
-                    if (unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_CALC_ERROR))) {
+                if(unlikely(rc->calculation)) {
+                    if(unlikely(!expression_evaluate(rc->calculation))) {
+                        // calculation failed
+                        rc->value = NAN;
                         rc->rrdcalc_flags |= RRDCALC_FLAG_CALC_ERROR;
-                        error("Health alarm '%s.%s': expression '%s' failed: %s",
-                              rc->chart?rc->chart:"NOCHART", rc->name, rc->calculation->parsed_as, buffer_tostring(rc->calculation->error_msg));
+
+                        debug(D_HEALTH
+                              , "Health on host '%s', alarm '%s.%s': expression '%s' failed: %s"
+                              , host->hostname
+                              , rc->chart ? rc->chart : "NOCHART"
+                              , rc->name
+                              , rc->calculation->parsed_as
+                              , buffer_tostring(rc->calculation->error_msg)
+                        );
                     }
-                }
-                else {
-                    if (unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_CALC_ERROR))
+                    else {
                         rc->rrdcalc_flags &= ~RRDCALC_FLAG_CALC_ERROR;
 
-                    debug(D_HEALTH, "Health alarm '%s.%s': expression '%s' gave value "
-                            CALCULATED_NUMBER_FORMAT
-                            ": %s (source: %s)",
-                          rc->chart?rc->chart:"NOCHART", rc->name,
-                          rc->calculation->parsed_as,
-                          rc->calculation->result,
-                          buffer_tostring(rc->calculation->error_msg),
-                          rc->source
-                    );
+                        debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': expression '%s' gave value " CALCULATED_NUMBER_FORMAT ": %s (source: %s)"
+                              , host->hostname
+                              , rc->chart ? rc->chart : "NOCHART"
+                              , rc->name
+                              , rc->calculation->parsed_as
+                              , rc->calculation->result
+                              , buffer_tostring(rc->calculation->error_msg)
+                              , rc->source
+                        );
 
-                    rc->value = rc->calculation->result;
+                        rc->value = rc->calculation->result;
+                    }
                 }
             }
-        }
-        rrdhost_unlock(&localhost);
+            rrdhost_unlock(host);
 
-        if(unlikely(runnable && !netdata_exit)) {
-            rrdhost_rdlock(&localhost);
+            if(unlikely(runnable && !netdata_exit)) {
+                rrdhost_rdlock(host);
 
-            for(rc = localhost.alarms; rc; rc = rc->next) {
-                if(unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_RUNNABLE)))
-                    continue;
-
-                int warning_status  = RRDCALC_STATUS_UNDEFINED;
-                int critical_status = RRDCALC_STATUS_UNDEFINED;
+                for(rc = host->alarms; rc; rc = rc->next) {
+                    if(unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_RUNNABLE)))
+                        continue;
 
-                if(likely(rc->warning)) {
-                    if(unlikely(!expression_evaluate(rc->warning))) {
-                        // calculation failed
+                    int warning_status  = RRDCALC_STATUS_UNDEFINED;
+                    int critical_status = RRDCALC_STATUS_UNDEFINED;
 
-                        debug(D_HEALTH, "Health alarm '%s.%s': warning expression failed with error: %s",
-                              rc->chart?rc->chart:"NOCHART", rc->name, buffer_tostring(rc->warning->error_msg));
+                    // --------------------------------------------------------
+                    // check the warning expression
 
-                        if (unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_WARN_ERROR))) {
+                    if(likely(rc->warning)) {
+                        if(unlikely(!expression_evaluate(rc->warning))) {
+                            // calculation failed
                             rc->rrdcalc_flags |= RRDCALC_FLAG_WARN_ERROR;
-                            error("Health alarm '%s.%s': warning expression failed with error: %s",
-                                  rc->chart?rc->chart:"NOCHART", rc->name, buffer_tostring(rc->warning->error_msg));
+
+                            debug(D_HEALTH
+                                  , "Health on host '%s', alarm '%s.%s': warning expression failed with error: %s"
+                                  , host->hostname
+                                  , rc->chart ? rc->chart : "NOCHART"
+                                  , rc->name
+                                  , buffer_tostring(rc->warning->error_msg)
+                            );
                         }
-                    }
-                    else {
-                        if(unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_WARN_ERROR))
+                        else {
                             rc->rrdcalc_flags &= ~RRDCALC_FLAG_WARN_ERROR;
-
-                        debug(D_HEALTH, "Health alarm '%s.%s': warning expression gave value "
-                                CALCULATED_NUMBER_FORMAT
-                                ": %s (source: %s)",
-                              rc->chart?rc->chart:"NOCHART", rc->name,
-                              rc->warning->result,
-                              buffer_tostring(rc->warning->error_msg),
-                              rc->source
-                        );
-
-                        warning_status = rrdcalc_value2status(rc->warning->result);
+                            debug(D_HEALTH
+                                  , "Health on host '%s', alarm '%s.%s': warning expression gave value " CALCULATED_NUMBER_FORMAT ": %s (source: %s)"
+                                  , host->hostname
+                                  , rc->chart ? rc->chart : "NOCHART"
+                                  , rc->name
+                                  , rc->warning->result
+                                  , buffer_tostring(rc->warning->error_msg)
+                                  , rc->source
+                            );
+                            warning_status = rrdcalc_value2status(rc->warning->result);
+                        }
                     }
-                }
-
-                if(likely(rc->critical)) {
-                    if(unlikely(!expression_evaluate(rc->critical))) {
-                        // calculation failed
 
-                        debug(D_HEALTH, "Health alarm '%s.%s': critical expression failed with error: %s",
-                              rc->chart?rc->chart:"NOCHART", rc->name, buffer_tostring(rc->critical->error_msg));
+                    // --------------------------------------------------------
+                    // check the critical expression
 
-                        if (unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_CRIT_ERROR))) {
+                    if(likely(rc->critical)) {
+                        if(unlikely(!expression_evaluate(rc->critical))) {
+                            // calculation failed
                             rc->rrdcalc_flags |= RRDCALC_FLAG_CRIT_ERROR;
-                            error("Health alarm '%s.%s': critical expression failed with error: %s",
-                                  rc->chart?rc->chart:"NOCHART", rc->name, buffer_tostring(rc->critical->error_msg));
+
+                            debug(D_HEALTH
+                                  , "Health on host '%s', alarm '%s.%s': critical expression failed with error: %s"
+                                  , host->hostname
+                                  , rc->chart ? rc->chart : "NOCHART"
+                                  , rc->name
+                                  , buffer_tostring(rc->critical->error_msg)
+                            );
                         }
-                    }
-                    else {
-                        if(unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_CRIT_ERROR))
+                        else {
                             rc->rrdcalc_flags &= ~RRDCALC_FLAG_CRIT_ERROR;
+                            debug(D_HEALTH
+                                  , "Health on host '%s', alarm '%s.%s': critical expression gave value " CALCULATED_NUMBER_FORMAT ": %s (source: %s)"
+                                  , host->hostname
+                                  , rc->chart ? rc->chart : "NOCHART"
+                                  , rc->name
+                                  , rc->critical->result
+                                  , buffer_tostring(rc->critical->error_msg)
+                                  , rc->source
+                            );
+                            critical_status = rrdcalc_value2status(rc->critical->result);
+                        }
+                    }
 
-                        debug(D_HEALTH, "Health alarm '%s.%s': critical expression gave value "
-                                CALCULATED_NUMBER_FORMAT
-                                ": %s (source: %s)",
-                              rc->chart?rc->chart:"NOCHART", rc->name,
-                              rc->critical->result,
-                              buffer_tostring(rc->critical->error_msg),
-                              rc->source
-                        );
+                    // --------------------------------------------------------
+                    // decide the final alarm status
+
+                    int status = RRDCALC_STATUS_UNDEFINED;
 
-                        critical_status = rrdcalc_value2status(rc->critical->result);
+                    switch(warning_status) {
+                        case RRDCALC_STATUS_CLEAR:
+                            status = RRDCALC_STATUS_CLEAR;
+                            break;
+
+                        case RRDCALC_STATUS_RAISED:
+                            status = RRDCALC_STATUS_WARNING;
+                            break;
+
+                        default:
+                            break;
                     }
-                }
 
-                int status = RRDCALC_STATUS_UNDEFINED;
+                    switch(critical_status) {
+                        case RRDCALC_STATUS_CLEAR:
+                            if(status == RRDCALC_STATUS_UNDEFINED)
+                                status = RRDCALC_STATUS_CLEAR;
+                            break;
 
-                switch(warning_status) {
-                    case RRDCALC_STATUS_CLEAR:
-                        status = RRDCALC_STATUS_CLEAR;
-                        break;
+                        case RRDCALC_STATUS_RAISED:
+                            status = RRDCALC_STATUS_CRITICAL;
+                            break;
 
-                    case RRDCALC_STATUS_RAISED:
-                        status = RRDCALC_STATUS_WARNING;
-                        break;
+                        default:
+                            break;
+                    }
 
-                    default:
-                        break;
-                }
+                    // --------------------------------------------------------
+                    // check if the new status and the old differ
 
-                switch(critical_status) {
-                    case RRDCALC_STATUS_CLEAR:
-                        if(status == RRDCALC_STATUS_UNDEFINED)
-                            status = RRDCALC_STATUS_CLEAR;
-                        break;
+                    if(status != rc->status) {
+                        int delay = 0;
 
-                    case RRDCALC_STATUS_RAISED:
-                        status = RRDCALC_STATUS_CRITICAL;
-                        break;
+                        // apply trigger hysteresis
 
-                    default:
-                        break;
-                }
+                        if(now > rc->delay_up_to_timestamp) {
+                            rc->delay_up_current = rc->delay_up_duration;
+                            rc->delay_down_current = rc->delay_down_duration;
+                            rc->delay_last = 0;
+                            rc->delay_up_to_timestamp = 0;
+                        }
+                        else {
+                            rc->delay_up_current = (int) (rc->delay_up_current * rc->delay_multiplier);
+                            if(rc->delay_up_current > rc->delay_max_duration)
+                                rc->delay_up_current = rc->delay_max_duration;
+
+                            rc->delay_down_current = (int) (rc->delay_down_current * rc->delay_multiplier);
+                            if(rc->delay_down_current > rc->delay_max_duration)
+                                rc->delay_down_current = rc->delay_max_duration;
+                        }
 
-                if(status != rc->status) {
-                    int delay = 0;
+                        if(status > rc->status)
+                            delay = rc->delay_up_current;
+                        else
+                            delay = rc->delay_down_current;
+
+                        // COMMENTED: because we do need to send raising alarms
+                        // if(now + delay < rc->delay_up_to_timestamp)
+                        //    delay = (int)(rc->delay_up_to_timestamp - now);
+
+                        rc->delay_last = delay;
+                        rc->delay_up_to_timestamp = now + delay;
+
+                        // add the alarm into the log
+
+                        health_alarm_log(
+                                host
+                                , rc->id
+                                , rc->next_event_id++
+                                , now
+                                , rc->name
+                                , rc->rrdset->id
+                                , rc->rrdset->family
+                                , rc->exec
+                                , rc->recipient
+                                , now - rc->last_status_change
+                                , rc->old_value
+                                , rc->value
+                                , rc->status
+                                , status
+                                , rc->source
+                                , rc->units
+                                , rc->info
+                                , rc->delay_last
+                                , (rc->options & RRDCALC_FLAG_NO_CLEAR_NOTIFICATION) ? HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION : 0
+                        );
 
-                    if(now > rc->delay_up_to_timestamp) {
-                        rc->delay_up_current = rc->delay_up_duration;
-                        rc->delay_down_current = rc->delay_down_duration;
-                        rc->delay_last = 0;
-                        rc->delay_up_to_timestamp = 0;
+                        rc->last_status_change = now;
+                        rc->status = status;
                     }
-                    else {
-                        rc->delay_up_current = (int)(rc->delay_up_current * rc->delay_multiplier);
-                        if(rc->delay_up_current > rc->delay_max_duration) rc->delay_up_current = rc->delay_max_duration;
 
-                        rc->delay_down_current = (int)(rc->delay_down_current * rc->delay_multiplier);
-                        if(rc->delay_down_current > rc->delay_max_duration) rc->delay_down_current = rc->delay_max_duration;
-                    }
+                    rc->last_updated = now;
+                    rc->next_update = now + rc->update_every;
 
-                    if(status > rc->status)
-                        delay = rc->delay_up_current;
-                    else
-                        delay = rc->delay_down_current;
-
-                    // COMMENTED: because we do need to send raising alarms
-                    // if(now + delay < rc->delay_up_to_timestamp)
-                    //    delay = (int)(rc->delay_up_to_timestamp - now);
-
-                    rc->delay_last = delay;
-                    rc->delay_up_to_timestamp = now + delay;
-                    health_alarm_log(
-                            &localhost,
-                            rc->id,
-                            rc->next_event_id++,
-                            now,
-                            rc->name,
-                            rc->rrdset->id,
-                            rc->rrdset->family,
-                            rc->exec,
-                            rc->recipient,
-                            now - rc->last_status_change,
-                            rc->old_value,
-                            rc->value,
-                            rc->status,
-                            status,
-                            rc->source,
-                            rc->units,
-                            rc->info,
-                            rc->delay_last,
-                            (rc->options & RRDCALC_FLAG_NO_CLEAR_NOTIFICATION)?HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION:0
-                    );
-                    rc->last_status_change = now;
-                    rc->status = status;
+                    if(next_run > rc->next_update)
+                        next_run = rc->next_update;
                 }
 
-                rc->last_updated = now;
-                rc->next_update = now + rc->update_every;
-
-                if (next_run > rc->next_update)
-                    next_run = rc->next_update;
+                rrdhost_unlock(host);
             }
 
-            rrdhost_unlock(&localhost);
-        }
+            if(unlikely(netdata_exit))
+                break;
 
-        if (unlikely(pthread_setcancelstate(oldstate, NULL) != 0))
-            error("Cannot set pthread cancel state to RESTORE (%d).", oldstate);
+            // execute notifications
+            // and cleanup
+            health_alarm_log_process(host);
 
-        if(unlikely(netdata_exit))
-            break;
+            if(unlikely(netdata_exit))
+                break;
+
+        } /* rrdhost_foreach */
+
+        rrd_unlock();
 
-        // execute notifications
-        // and cleanup
-        health_alarm_log_process(&localhost);
+        if(unlikely(pthread_setcancelstate(oldstate, NULL) != 0))
+            error("Cannot set pthread cancel state to RESTORE (%d).", oldstate);
 
         if(unlikely(netdata_exit))
             break;
-        
+
         now = now_realtime_sec();
         if(now < next_run) {
-            debug(D_HEALTH, "Health monitoring iteration no %u done. Next iteration in %d secs",
-                  loop, (int) (next_run - now));
+            debug(D_HEALTH, "Health monitoring iteration no %u done. Next iteration in %d secs", loop, (int) (next_run - now));
             sleep_usec(USEC_PER_SEC * (usec_t) (next_run - now));
+            now = now_realtime_sec();
         }
-        else {
+        else
             debug(D_HEALTH, "Health monitoring iteration no %u done. Next iteration now", loop);
-        }
-    }
+
+        now_boottime = now_boottime_sec();
+
+    } // forever
 
     buffer_free(wb);
 
index 87f8a1a18e547e2665cd5b007e3483dfcb361a06..f8e2903234144d9b2eb823d2e0c3dd15ada323ab 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef NETDATA_HEALTH_H
 #define NETDATA_HEALTH_H
 
-extern int health_enabled;
+extern int default_health_enabled;
 
 extern int rrdvar_compare(void *a, void *b);
 
@@ -369,4 +369,58 @@ extern void rrdvar_custom_host_variable_set(RRDVAR *rv, calculated_number value)
 
 extern const char *rrdcalc_status2string(int status);
 
+
+extern int health_alarm_log_open(RRDHOST *host);
+extern void health_alarm_log_close(RRDHOST *host);
+extern void health_log_rotate(RRDHOST *host);
+extern void health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae);
+extern ssize_t health_alarm_log_read(RRDHOST *host, FILE *fp, const char *filename);
+extern void health_alarm_log_load(RRDHOST *host);
+extern void health_alarm_log(
+        RRDHOST *host,
+        uint32_t alarm_id,
+        uint32_t alarm_event_id,
+        time_t when,
+        const char *name,
+        const char *chart,
+        const char *family,
+        const char *exec,
+        const char *recipient,
+        time_t duration,
+        calculated_number old_value,
+        calculated_number new_value,
+        int old_status,
+        int new_status,
+        const char *source,
+        const char *units,
+        const char *info,
+        int delay,
+        uint32_t flags
+);
+
+extern void health_readdir(RRDHOST *host, const char *path);
+extern char *health_config_dir(void);
+extern void health_reload_host(RRDHOST *host);
+extern void health_alarm_log_free(RRDHOST *host);
+
+extern void rrdcalc_free(RRDHOST *host, RRDCALC *rc);
+extern void rrdcalctemplate_free(RRDHOST *host, RRDCALCTEMPLATE *rt);
+
+#ifdef NETDATA_HEALTH_INTERNALS
+#define RRDVAR_MAX_LENGTH 1024
+
+extern int rrdcalc_exists(RRDHOST *host, const char *chart, const char *name, uint32_t hash_chart, uint32_t hash_name);
+extern uint32_t rrdcalc_get_unique_id(RRDHOST *host, const char *chart, const char *name, uint32_t *next_event_id);
+extern int rrdvar_fix_name(char *variable);
+
+extern RRDCALC *rrdcalc_create(RRDHOST *host, RRDCALCTEMPLATE *rt, const char *chart);
+extern void rrdcalc_create_part2(RRDHOST *host, RRDCALC *rc);
+
+extern RRDVAR *rrdvar_create_and_index(const char *scope, avl_tree_lock *tree, const char *name, int type, void *value);
+extern void rrdvar_free(RRDHOST *host, avl_tree_lock *tree, RRDVAR *rv);
+
+extern void health_alarm_log_free_one_nochecks_nounlink(ALARM_ENTRY *ae);
+
+#endif // NETDATA_HEALTH_INTERNALS
+
 #endif //NETDATA_HEALTH_H
diff --git a/src/health_config.c b/src/health_config.c
new file mode 100644 (file)
index 0000000..ad954cb
--- /dev/null
@@ -0,0 +1,877 @@
+#define NETDATA_HEALTH_INTERNALS
+#include "common.h"
+
+#define HEALTH_CONF_MAX_LINE 4096
+
+#define HEALTH_ALARM_KEY "alarm"
+#define HEALTH_TEMPLATE_KEY "template"
+#define HEALTH_ON_KEY "on"
+#define HEALTH_FAMILIES_KEY "families"
+#define HEALTH_LOOKUP_KEY "lookup"
+#define HEALTH_CALC_KEY "calc"
+#define HEALTH_EVERY_KEY "every"
+#define HEALTH_GREEN_KEY "green"
+#define HEALTH_RED_KEY "red"
+#define HEALTH_WARN_KEY "warn"
+#define HEALTH_CRIT_KEY "crit"
+#define HEALTH_EXEC_KEY "exec"
+#define HEALTH_RECIPIENT_KEY "to"
+#define HEALTH_UNITS_KEY "units"
+#define HEALTH_INFO_KEY "info"
+#define HEALTH_DELAY_KEY "delay"
+#define HEALTH_OPTIONS_KEY "options"
+
+static inline int rrdcalc_add_alarm_from_config(RRDHOST *host, RRDCALC *rc) {
+    if(!rc->chart) {
+        error("Health configuration for alarm '%s' does not have a chart", rc->name);
+        return 0;
+    }
+
+    if(!rc->update_every) {
+        error("Health configuration for alarm '%s.%s' has no frequency (parameter 'every'). Ignoring it.", rc->chart?rc->chart:"NOCHART", rc->name);
+        return 0;
+    }
+
+    if(!RRDCALC_HAS_DB_LOOKUP(rc) && !rc->warning && !rc->critical) {
+        error("Health configuration for alarm '%s.%s' is useless (no calculation, no warning and no critical evaluation)", rc->chart?rc->chart:"NOCHART", rc->name);
+        return 0;
+    }
+
+    if (rrdcalc_exists(host, rc->chart, rc->name, rc->hash_chart, rc->hash))
+        return 0;
+
+    rc->id = rrdcalc_get_unique_id(host, rc->chart, rc->name, &rc->next_event_id);
+
+    debug(D_HEALTH, "Health configuration adding alarm '%s.%s' (%u): exec '%s', recipient '%s', green %Lf, red %Lf, lookup: group %d, after %d, before %d, options %u, dimensions '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s', delay up %d, delay down %d, delay max %d, delay_multiplier %f",
+            rc->chart?rc->chart:"NOCHART",
+            rc->name,
+            rc->id,
+            (rc->exec)?rc->exec:"DEFAULT",
+            (rc->recipient)?rc->recipient:"DEFAULT",
+            rc->green,
+            rc->red,
+            rc->group,
+            rc->after,
+            rc->before,
+            rc->options,
+            (rc->dimensions)?rc->dimensions:"NONE",
+            rc->update_every,
+            (rc->calculation)?rc->calculation->parsed_as:"NONE",
+            (rc->warning)?rc->warning->parsed_as:"NONE",
+            (rc->critical)?rc->critical->parsed_as:"NONE",
+            rc->source,
+            rc->delay_up_duration,
+            rc->delay_down_duration,
+            rc->delay_max_duration,
+            rc->delay_multiplier
+    );
+
+    rrdcalc_create_part2(host, rc);
+    return 1;
+}
+
+static inline int rrdcalctemplate_add_template_from_config(RRDHOST *host, RRDCALCTEMPLATE *rt) {
+    if(unlikely(!rt->context)) {
+        error("Health configuration for template '%s' does not have a context", rt->name);
+        return 0;
+    }
+
+    if(unlikely(!rt->update_every)) {
+        error("Health configuration for template '%s' has no frequency (parameter 'every'). Ignoring it.", rt->name);
+        return 0;
+    }
+
+    if(unlikely(!RRDCALCTEMPLATE_HAS_CALCULATION(rt) && !rt->warning && !rt->critical)) {
+        error("Health configuration for template '%s' is useless (no calculation, no warning and no critical evaluation)", rt->name);
+        return 0;
+    }
+
+    RRDCALCTEMPLATE *t, *last = NULL;
+    for (t = host->templates; t ; last = t, t = t->next) {
+        if(unlikely(t->hash_name == rt->hash_name && !strcmp(t->name, rt->name))) {
+            error("Health configuration template '%s' already exists for host '%s'.", rt->name, host->hostname);
+            return 0;
+        }
+    }
+
+    debug(D_HEALTH, "Health configuration adding template '%s': context '%s', exec '%s', recipient '%s', green %Lf, red %Lf, lookup: group %d, after %d, before %d, options %u, dimensions '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s', delay up %d, delay down %d, delay max %d, delay_multiplier %f",
+            rt->name,
+            (rt->context)?rt->context:"NONE",
+            (rt->exec)?rt->exec:"DEFAULT",
+            (rt->recipient)?rt->recipient:"DEFAULT",
+            rt->green,
+            rt->red,
+            rt->group,
+            rt->after,
+            rt->before,
+            rt->options,
+            (rt->dimensions)?rt->dimensions:"NONE",
+            rt->update_every,
+            (rt->calculation)?rt->calculation->parsed_as:"NONE",
+            (rt->warning)?rt->warning->parsed_as:"NONE",
+            (rt->critical)?rt->critical->parsed_as:"NONE",
+            rt->source,
+            rt->delay_up_duration,
+            rt->delay_down_duration,
+            rt->delay_max_duration,
+            rt->delay_multiplier
+    );
+
+    if(likely(last)) {
+        last->next = rt;
+    }
+    else {
+        rt->next = host->templates;
+        host->templates = rt;
+    }
+
+    return 1;
+}
+
+static inline int health_parse_duration(char *string, int *result) {
+    // make sure it is a number
+    if(!*string || !(isdigit(*string) || *string == '+' || *string == '-')) {
+        *result = 0;
+        return 0;
+    }
+
+    char *e = NULL;
+    calculated_number n = strtold(string, &e);
+    if(e && *e) {
+        switch (*e) {
+            case 'Y':
+                *result = (int) (n * 86400 * 365);
+                break;
+            case 'M':
+                *result = (int) (n * 86400 * 30);
+                break;
+            case 'w':
+                *result = (int) (n * 86400 * 7);
+                break;
+            case 'd':
+                *result = (int) (n * 86400);
+                break;
+            case 'h':
+                *result = (int) (n * 3600);
+                break;
+            case 'm':
+                *result = (int) (n * 60);
+                break;
+
+            default:
+            case 's':
+                *result = (int) (n);
+                break;
+        }
+    }
+    else
+        *result = (int)(n);
+
+    return 1;
+}
+
+static inline int health_parse_delay(
+        size_t line, const char *path, const char *file, char *string,
+        int *delay_up_duration,
+        int *delay_down_duration,
+        int *delay_max_duration,
+        float *delay_multiplier) {
+
+    char given_up = 0;
+    char given_down = 0;
+    char given_max = 0;
+    char given_multiplier = 0;
+
+    char *s = string;
+    while(*s) {
+        char *key = s;
+
+        while(*s && !isspace(*s)) s++;
+        while(*s && isspace(*s)) *s++ = '\0';
+
+        if(!*key) break;
+
+        char *value = s;
+        while(*s && !isspace(*s)) s++;
+        while(*s && isspace(*s)) *s++ = '\0';
+
+        if(!strcasecmp(key, "up")) {
+            if (!health_parse_duration(value, delay_up_duration)) {
+                error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword",
+                        line, path, file, value, key);
+            }
+            else given_up = 1;
+        }
+        else if(!strcasecmp(key, "down")) {
+            if (!health_parse_duration(value, delay_down_duration)) {
+                error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword",
+                        line, path, file, value, key);
+            }
+            else given_down = 1;
+        }
+        else if(!strcasecmp(key, "multiplier")) {
+            *delay_multiplier = strtof(value, NULL);
+            if(isnan(*delay_multiplier) || isinf(*delay_multiplier) || islessequal(*delay_multiplier, 0)) {
+                error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword",
+                        line, path, file, value, key);
+            }
+            else given_multiplier = 1;
+        }
+        else if(!strcasecmp(key, "max")) {
+            if (!health_parse_duration(value, delay_max_duration)) {
+                error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword",
+                        line, path, file, value, key);
+            }
+            else given_max = 1;
+        }
+        else {
+            error("Health configuration at line %zu of file '%s/%s': unknown keyword '%s'",
+                    line, path, file, key);
+        }
+    }
+
+    if(!given_up)
+        *delay_up_duration = 0;
+
+    if(!given_down)
+        *delay_down_duration = 0;
+
+    if(!given_multiplier)
+        *delay_multiplier = 1.0;
+
+    if(!given_max) {
+        if((*delay_max_duration) < (*delay_up_duration) * (*delay_multiplier))
+            *delay_max_duration = (*delay_up_duration) * (*delay_multiplier);
+
+        if((*delay_max_duration) < (*delay_down_duration) * (*delay_multiplier))
+            *delay_max_duration = (*delay_down_duration) * (*delay_multiplier);
+    }
+
+    return 1;
+}
+
+static inline uint32_t health_parse_options(const char *s) {
+    uint32_t options = 0;
+    char buf[100+1] = "";
+
+    while(*s) {
+        buf[0] = '\0';
+
+        // skip spaces
+        while(*s && isspace(*s))
+            s++;
+
+        // find the next space
+        size_t count = 0;
+        while(*s && count < 100 && !isspace(*s))
+            buf[count++] = *s++;
+
+        if(buf[0]) {
+            buf[count] = '\0';
+
+            if(!strcasecmp(buf, "no-clear-notification") || !strcasecmp(buf, "no-clear"))
+                options |= RRDCALC_FLAG_NO_CLEAR_NOTIFICATION;
+            else
+                error("Ignoring unknown alarm option '%s'", buf);
+        }
+    }
+
+    return options;
+}
+
+static inline int health_parse_db_lookup(
+        size_t line, const char *path, const char *file, char *string,
+        int *group_method, int *after, int *before, int *every,
+        uint32_t *options, char **dimensions
+) {
+    debug(D_HEALTH, "Health configuration parsing database lookup %zu@%s/%s: %s", line, path, file, string);
+
+    if(*dimensions) freez(*dimensions);
+    *dimensions = NULL;
+    *after = 0;
+    *before = 0;
+    *every = 0;
+    *options = 0;
+
+    char *s = string, *key;
+
+    // first is the group method
+    key = s;
+    while(*s && !isspace(*s)) s++;
+    while(*s && isspace(*s)) *s++ = '\0';
+    if(!*s) {
+        error("Health configuration invalid chart calculation at line %zu of file '%s/%s': expected group method followed by the 'after' time, but got '%s'",
+                line, path, file, key);
+        return 0;
+    }
+
+    if((*group_method = web_client_api_request_v1_data_group(key, -1)) == -1) {
+        error("Health configuration at line %zu of file '%s/%s': invalid group method '%s'",
+                line, path, file, key);
+        return 0;
+    }
+
+    // then is the 'after' time
+    key = s;
+    while(*s && !isspace(*s)) s++;
+    while(*s && isspace(*s)) *s++ = '\0';
+
+    if(!health_parse_duration(key, after)) {
+        error("Health configuration at line %zu of file '%s/%s': invalid duration '%s' after group method",
+                line, path, file, key);
+        return 0;
+    }
+
+    // sane defaults
+    *every = abs(*after);
+
+    // now we may have optional parameters
+    while(*s) {
+        key = s;
+        while(*s && !isspace(*s)) s++;
+        while(*s && isspace(*s)) *s++ = '\0';
+        if(!*key) break;
+
+        if(!strcasecmp(key, "at")) {
+            char *value = s;
+            while(*s && !isspace(*s)) s++;
+            while(*s && isspace(*s)) *s++ = '\0';
+
+            if (!health_parse_duration(value, before)) {
+                error("Health configuration at line %zu of file '%s/%s': invalid duration '%s' for '%s' keyword",
+                        line, path, file, value, key);
+            }
+        }
+        else if(!strcasecmp(key, HEALTH_EVERY_KEY)) {
+            char *value = s;
+            while(*s && !isspace(*s)) s++;
+            while(*s && isspace(*s)) *s++ = '\0';
+
+            if (!health_parse_duration(value, every)) {
+                error("Health configuration at line %zu of file '%s/%s': invalid duration '%s' for '%s' keyword",
+                        line, path, file, value, key);
+            }
+        }
+        else if(!strcasecmp(key, "absolute") || !strcasecmp(key, "abs") || !strcasecmp(key, "absolute_sum")) {
+            *options |= RRDR_OPTION_ABSOLUTE;
+        }
+        else if(!strcasecmp(key, "min2max")) {
+            *options |= RRDR_OPTION_MIN2MAX;
+        }
+        else if(!strcasecmp(key, "null2zero")) {
+            *options |= RRDR_OPTION_NULL2ZERO;
+        }
+        else if(!strcasecmp(key, "percentage")) {
+            *options |= RRDR_OPTION_PERCENTAGE;
+        }
+        else if(!strcasecmp(key, "unaligned")) {
+            *options |= RRDR_OPTION_NOT_ALIGNED;
+        }
+        else if(!strcasecmp(key, "of")) {
+            if(*s && strcasecmp(s, "all"))
+                *dimensions = strdupz(s);
+            break;
+        }
+        else {
+            error("Health configuration at line %zu of file '%s/%s': unknown keyword '%s'",
+                    line, path, file, key);
+        }
+    }
+
+    return 1;
+}
+
+static inline char *trim_all_spaces(char *buffer) {
+    char *d = buffer, *s = buffer;
+
+    // skip spaces
+    while(isspace(*s)) s++;
+
+    while(*s) {
+        // copy the non-space part
+        while(*s && !isspace(*s)) *d++ = *s++;
+
+        // add a space if we have to
+        if(*s && isspace(*s)) {
+            *d++ = ' ';
+            s++;
+        }
+
+        // skip spaces
+        while(isspace(*s)) s++;
+    }
+
+    *d = '\0';
+
+    if(d > buffer) {
+        d--;
+        if(isspace(*d)) *d = '\0';
+    }
+
+    if(!buffer[0]) return NULL;
+    return buffer;
+}
+
+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);
+    return strdupz(buffer);
+}
+
+static inline void strip_quotes(char *s) {
+    while(*s) {
+        if(*s == '\'' || *s == '"') *s = ' ';
+        s++;
+    }
+}
+
+int health_readfile(RRDHOST *host, const char *path, const char *filename) {
+    debug(D_HEALTH, "Health configuration reading file '%s/%s'", path, filename);
+
+    static uint32_t
+            hash_alarm = 0,
+            hash_template = 0,
+            hash_on = 0,
+            hash_families = 0,
+            hash_calc = 0,
+            hash_green = 0,
+            hash_red = 0,
+            hash_warn = 0,
+            hash_crit = 0,
+            hash_exec = 0,
+            hash_every = 0,
+            hash_lookup = 0,
+            hash_units = 0,
+            hash_info = 0,
+            hash_recipient = 0,
+            hash_delay = 0,
+            hash_options = 0;
+
+    char buffer[HEALTH_CONF_MAX_LINE + 1];
+
+    if(unlikely(!hash_alarm)) {
+        hash_alarm = simple_uhash(HEALTH_ALARM_KEY);
+        hash_template = simple_uhash(HEALTH_TEMPLATE_KEY);
+        hash_on = simple_uhash(HEALTH_ON_KEY);
+        hash_families = simple_uhash(HEALTH_FAMILIES_KEY);
+        hash_calc = simple_uhash(HEALTH_CALC_KEY);
+        hash_lookup = simple_uhash(HEALTH_LOOKUP_KEY);
+        hash_green = simple_uhash(HEALTH_GREEN_KEY);
+        hash_red = simple_uhash(HEALTH_RED_KEY);
+        hash_warn = simple_uhash(HEALTH_WARN_KEY);
+        hash_crit = simple_uhash(HEALTH_CRIT_KEY);
+        hash_exec = simple_uhash(HEALTH_EXEC_KEY);
+        hash_every = simple_uhash(HEALTH_EVERY_KEY);
+        hash_units = simple_hash(HEALTH_UNITS_KEY);
+        hash_info = simple_hash(HEALTH_INFO_KEY);
+        hash_recipient = simple_hash(HEALTH_RECIPIENT_KEY);
+        hash_delay = simple_uhash(HEALTH_DELAY_KEY);
+        hash_options = simple_uhash(HEALTH_OPTIONS_KEY);
+    }
+
+    snprintfz(buffer, HEALTH_CONF_MAX_LINE, "%s/%s", path, filename);
+    FILE *fp = fopen(buffer, "r");
+    if(!fp) {
+        error("Health configuration cannot read file '%s'.", buffer);
+        return 0;
+    }
+
+    RRDCALC *rc = NULL;
+    RRDCALCTEMPLATE *rt = NULL;
+
+    size_t line = 0, append = 0;
+    char *s;
+    while((s = fgets(&buffer[append], (int)(HEALTH_CONF_MAX_LINE - append), fp)) || append) {
+        int stop_appending = !s;
+        line++;
+        s = trim(buffer);
+        if(!s) continue;
+
+        append = strlen(s);
+        if(!stop_appending && s[append - 1] == '\\') {
+            s[append - 1] = ' ';
+            append = &s[append] - buffer;
+            if(append < HEALTH_CONF_MAX_LINE)
+                continue;
+            else {
+                error("Health configuration has too long muli-line at line %zu of file '%s/%s'.", line, path, filename);
+            }
+        }
+        append = 0;
+
+        char *key = s;
+        while(*s && *s != ':') s++;
+        if(!*s) {
+            error("Health configuration has invalid line %zu of file '%s/%s'. It does not contain a ':'. Ignoring it.", line, path, filename);
+            continue;
+        }
+        *s = '\0';
+        s++;
+
+        char *value = s;
+        key = trim_all_spaces(key);
+        value = trim_all_spaces(value);
+
+        if(!key) {
+            error("Health configuration has invalid line %zu of file '%s/%s'. Keyword is empty. Ignoring it.", line, path, filename);
+            continue;
+        }
+
+        if(!value) {
+            error("Health configuration has invalid line %zu of file '%s/%s'. value is empty. Ignoring it.", line, path, filename);
+            continue;
+        }
+
+        uint32_t hash = simple_uhash(key);
+
+        if(hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) {
+            if(rc && !rrdcalc_add_alarm_from_config(host, rc))
+                rrdcalc_free(host, rc);
+
+            if(rt) {
+                if (!rrdcalctemplate_add_template_from_config(host, rt))
+                    rrdcalctemplate_free(host, rt);
+                rt = NULL;
+            }
+
+            rc = callocz(1, sizeof(RRDCALC));
+            rc->next_event_id = 1;
+            rc->name = strdupz(value);
+            rc->hash = simple_hash(rc->name);
+            rc->source = health_source_file(line, path, filename);
+            rc->green = NAN;
+            rc->red = NAN;
+            rc->value = NAN;
+            rc->old_value = NAN;
+            rc->delay_multiplier = 1.0;
+
+            if(rrdvar_fix_name(rc->name))
+                error("Health configuration renamed alarm '%s' to '%s'", value, rc->name);
+        }
+        else if(hash == hash_template && !strcasecmp(key, HEALTH_TEMPLATE_KEY)) {
+            if(rc) {
+                if(!rrdcalc_add_alarm_from_config(host, rc))
+                    rrdcalc_free(host, rc);
+                rc = NULL;
+            }
+
+            if(rt && !rrdcalctemplate_add_template_from_config(host, rt))
+                rrdcalctemplate_free(host, rt);
+
+            rt = callocz(1, sizeof(RRDCALCTEMPLATE));
+            rt->name = strdupz(value);
+            rt->hash_name = simple_hash(rt->name);
+            rt->source = health_source_file(line, path, filename);
+            rt->green = NAN;
+            rt->red = NAN;
+            rt->delay_multiplier = 1.0;
+
+            if(rrdvar_fix_name(rt->name))
+                error("Health configuration renamed template '%s' to '%s'", value, rt->name);
+        }
+        else if(rc) {
+            if(hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) {
+                if(rc->chart) {
+                    if(strcmp(rc->chart, value))
+                        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->hash_chart = simple_hash(rc->chart);
+            }
+            else if(hash == hash_lookup && !strcasecmp(key, HEALTH_LOOKUP_KEY)) {
+                health_parse_db_lookup(line, path, filename, value, &rc->group, &rc->after, &rc->before,
+                        &rc->update_every,
+                        &rc->options, &rc->dimensions);
+            }
+            else if(hash == hash_every && !strcasecmp(key, HEALTH_EVERY_KEY)) {
+                if(!health_parse_duration(value, &rc->update_every))
+                    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) {
+                    error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.",
+                            line, path, filename, rc->name, key, e);
+                }
+            }
+            else if(hash == hash_red && !strcasecmp(key, HEALTH_RED_KEY)) {
+                char *e;
+                rc->red = strtold(value, &e);
+                if(e && *e) {
+                    error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.",
+                            line, path, filename, rc->name, key, e);
+                }
+            }
+            else if(hash == hash_calc && !strcasecmp(key, HEALTH_CALC_KEY)) {
+                const char *failed_at = NULL;
+                int error = 0;
+                rc->calculation = expression_parse(value, &failed_at, &error);
+                if(!rc->calculation) {
+                    error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
+                            line, path, filename, rc->name, key, value, expression_strerror(error), failed_at);
+                }
+            }
+            else if(hash == hash_warn && !strcasecmp(key, HEALTH_WARN_KEY)) {
+                const char *failed_at = NULL;
+                int error = 0;
+                rc->warning = expression_parse(value, &failed_at, &error);
+                if(!rc->warning) {
+                    error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
+                            line, path, filename, rc->name, key, value, expression_strerror(error), failed_at);
+                }
+            }
+            else if(hash == hash_crit && !strcasecmp(key, HEALTH_CRIT_KEY)) {
+                const char *failed_at = NULL;
+                int error = 0;
+                rc->critical = expression_parse(value, &failed_at, &error);
+                if(!rc->critical) {
+                    error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
+                            line, path, filename, rc->name, key, value, expression_strerror(error), failed_at);
+                }
+            }
+            else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) {
+                if(rc->exec) {
+                    if(strcmp(rc->exec, value))
+                        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);
+            }
+            else if(hash == hash_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) {
+                if(rc->recipient) {
+                    if(strcmp(rc->recipient, value))
+                        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);
+            }
+            else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) {
+                if(rc->units) {
+                    if(strcmp(rc->units, value))
+                        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);
+                strip_quotes(rc->units);
+            }
+            else if(hash == hash_info && !strcasecmp(key, HEALTH_INFO_KEY)) {
+                if(rc->info) {
+                    if(strcmp(rc->info, value))
+                        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);
+                strip_quotes(rc->info);
+            }
+            else if(hash == hash_delay && !strcasecmp(key, HEALTH_DELAY_KEY)) {
+                health_parse_delay(line, path, filename, value, &rc->delay_up_duration, &rc->delay_down_duration, &rc->delay_max_duration, &rc->delay_multiplier);
+            }
+            else if(hash == hash_options && !strcasecmp(key, HEALTH_OPTIONS_KEY)) {
+                rc->options |= health_parse_options(value);
+            }
+            else {
+                error("Health configuration at line %zu of file '%s/%s' for alarm '%s' has unknown key '%s'.",
+                        line, path, filename, rc->name, key);
+            }
+        }
+        else if(rt) {
+            if(hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) {
+                if(rt->context) {
+                    if(strcmp(rt->context, value))
+                        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->hash_context = simple_hash(rt->context);
+            }
+            else if(hash == hash_families && !strcasecmp(key, HEALTH_FAMILIES_KEY)) {
+                freez(rt->family_match);
+                simple_pattern_free(rt->family_pattern);
+
+                rt->family_match = strdupz(value);
+                rt->family_pattern = simple_pattern_create(rt->family_match, SIMPLE_PATTERN_EXACT);
+            }
+            else if(hash == hash_lookup && !strcasecmp(key, HEALTH_LOOKUP_KEY)) {
+                health_parse_db_lookup(line, path, filename, value, &rt->group, &rt->after, &rt->before,
+                        &rt->update_every, &rt->options, &rt->dimensions);
+            }
+            else if(hash == hash_every && !strcasecmp(key, HEALTH_EVERY_KEY)) {
+                if(!health_parse_duration(value, &rt->update_every))
+                    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) {
+                    error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.",
+                            line, path, filename, rt->name, key, e);
+                }
+            }
+            else if(hash == hash_red && !strcasecmp(key, HEALTH_RED_KEY)) {
+                char *e;
+                rt->red = strtold(value, &e);
+                if(e && *e) {
+                    error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.",
+                            line, path, filename, rt->name, key, e);
+                }
+            }
+            else if(hash == hash_calc && !strcasecmp(key, HEALTH_CALC_KEY)) {
+                const char *failed_at = NULL;
+                int error = 0;
+                rt->calculation = expression_parse(value, &failed_at, &error);
+                if(!rt->calculation) {
+                    error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
+                            line, path, filename, rt->name, key, value, expression_strerror(error), failed_at);
+                }
+            }
+            else if(hash == hash_warn && !strcasecmp(key, HEALTH_WARN_KEY)) {
+                const char *failed_at = NULL;
+                int error = 0;
+                rt->warning = expression_parse(value, &failed_at, &error);
+                if(!rt->warning) {
+                    error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
+                            line, path, filename, rt->name, key, value, expression_strerror(error), failed_at);
+                }
+            }
+            else if(hash == hash_crit && !strcasecmp(key, HEALTH_CRIT_KEY)) {
+                const char *failed_at = NULL;
+                int error = 0;
+                rt->critical = expression_parse(value, &failed_at, &error);
+                if(!rt->critical) {
+                    error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
+                            line, path, filename, rt->name, key, value, expression_strerror(error), failed_at);
+                }
+            }
+            else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) {
+                if(rt->exec) {
+                    if(strcmp(rt->exec, value))
+                        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);
+            }
+            else if(hash == hash_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) {
+                if(rt->recipient) {
+                    if(strcmp(rt->recipient, value))
+                        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);
+            }
+            else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) {
+                if(rt->units) {
+                    if(strcmp(rt->units, value))
+                        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);
+                strip_quotes(rt->units);
+            }
+            else if(hash == hash_info && !strcasecmp(key, HEALTH_INFO_KEY)) {
+                if(rt->info) {
+                    if(strcmp(rt->info, value))
+                        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);
+                strip_quotes(rt->info);
+            }
+            else if(hash == hash_delay && !strcasecmp(key, HEALTH_DELAY_KEY)) {
+                health_parse_delay(line, path, filename, value, &rt->delay_up_duration, &rt->delay_down_duration, &rt->delay_max_duration, &rt->delay_multiplier);
+            }
+            else if(hash == hash_options && !strcasecmp(key, HEALTH_OPTIONS_KEY)) {
+                rt->options |= health_parse_options(value);
+            }
+            else {
+                error("Health configuration at line %zu of file '%s/%s' for template '%s' has unknown key '%s'.",
+                        line, path, filename, rt->name, key);
+            }
+        }
+        else {
+            error("Health configuration at line %zu of file '%s/%s' has unknown key '%s'. Expected either '" HEALTH_ALARM_KEY "' or '" HEALTH_TEMPLATE_KEY "'.",
+                    line, path, filename, key);
+        }
+    }
+
+    if(rc && !rrdcalc_add_alarm_from_config(host, rc))
+        rrdcalc_free(host, rc);
+
+    if(rt && !rrdcalctemplate_add_template_from_config(host, rt))
+        rrdcalctemplate_free(host, rt);
+
+    fclose(fp);
+    return 1;
+}
+
+void health_readdir(RRDHOST *host, const char *path) {
+    if(!host->health_enabled) return;
+
+    size_t pathlen = strlen(path);
+
+    debug(D_HEALTH, "Health configuration reading directory '%s'", path);
+
+    DIR *dir = opendir(path);
+    if (!dir) {
+        error("Health configuration cannot open directory '%s'.", path);
+        return;
+    }
+
+    struct dirent *de = NULL;
+    while ((de = readdir(dir))) {
+        size_t len = strlen(de->d_name);
+
+        if(de->d_type == DT_DIR
+           && (
+                   (de->d_name[0] == '.' && de->d_name[1] == '\0')
+                   || (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
+           )) {
+            debug(D_HEALTH, "Ignoring directory '%s'", de->d_name);
+            continue;
+        }
+
+        else if(de->d_type == DT_DIR) {
+            char *s = mallocz(pathlen + strlen(de->d_name) + 2);
+            strcpy(s, path);
+            strcat(s, "/");
+            strcat(s, de->d_name);
+            health_readdir(host, s);
+            freez(s);
+            continue;
+        }
+
+        else if((de->d_type == DT_LNK || de->d_type == DT_REG || de->d_type == DT_UNKNOWN) &&
+                len > 5 && !strcmp(&de->d_name[len - 5], ".conf")) {
+            health_readfile(host, path, de->d_name);
+        }
+
+        else debug(D_HEALTH, "Ignoring file '%s'", de->d_name);
+    }
+
+    closedir(dir);
+}
+
+
diff --git a/src/health_json.c b/src/health_json.c
new file mode 100644 (file)
index 0000000..4d24d5d
--- /dev/null
@@ -0,0 +1,256 @@
+#define NETDATA_HEALTH_INTERNALS
+#include "common.h"
+
+static inline void health_string2json(BUFFER *wb, const char *prefix, const char *label, const char *value, const char *suffix) {
+    if(value && *value)
+        buffer_sprintf(wb, "%s\"%s\":\"%s\"%s", prefix, label, value, suffix);
+    else
+        buffer_sprintf(wb, "%s\"%s\":null%s", prefix, label, suffix);
+}
+
+static inline void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae, RRDHOST *host) {
+    buffer_sprintf(wb,
+            "\n\t{\n"
+                    "\t\t\"hostname\": \"%s\",\n"
+                    "\t\t\"unique_id\": %u,\n"
+                    "\t\t\"alarm_id\": %u,\n"
+                    "\t\t\"alarm_event_id\": %u,\n"
+                    "\t\t\"name\": \"%s\",\n"
+                    "\t\t\"chart\": \"%s\",\n"
+                    "\t\t\"family\": \"%s\",\n"
+                    "\t\t\"processed\": %s,\n"
+                    "\t\t\"updated\": %s,\n"
+                    "\t\t\"exec_run\": %lu,\n"
+                    "\t\t\"exec_failed\": %s,\n"
+                    "\t\t\"exec\": \"%s\",\n"
+                    "\t\t\"recipient\": \"%s\",\n"
+                    "\t\t\"exec_code\": %d,\n"
+                    "\t\t\"source\": \"%s\",\n"
+                    "\t\t\"units\": \"%s\",\n"
+                    "\t\t\"info\": \"%s\",\n"
+                    "\t\t\"when\": %lu,\n"
+                    "\t\t\"duration\": %lu,\n"
+                    "\t\t\"non_clear_duration\": %lu,\n"
+                    "\t\t\"status\": \"%s\",\n"
+                    "\t\t\"old_status\": \"%s\",\n"
+                    "\t\t\"delay\": %d,\n"
+                    "\t\t\"delay_up_to_timestamp\": %lu,\n"
+                    "\t\t\"updated_by_id\": %u,\n"
+                    "\t\t\"updates_id\": %u,\n"
+                    "\t\t\"value_string\": \"%s\",\n"
+                    "\t\t\"old_value_string\": \"%s\",\n"
+                   , host->hostname
+                   , ae->unique_id
+                   , ae->alarm_id
+                   , ae->alarm_event_id
+                   , ae->name
+                   , ae->chart
+                   , ae->family
+                   , (ae->flags & HEALTH_ENTRY_FLAG_PROCESSED)?"true":"false"
+                   , (ae->flags & HEALTH_ENTRY_FLAG_UPDATED)?"true":"false"
+                   , (unsigned long)ae->exec_run_timestamp
+                   , (ae->flags & HEALTH_ENTRY_FLAG_EXEC_FAILED)?"true":"false"
+                   , ae->exec?ae->exec:host->health_default_exec
+                   , ae->recipient?ae->recipient:host->health_default_recipient
+                   , ae->exec_code
+                   , ae->source
+                   , ae->units?ae->units:""
+                   , ae->info?ae->info:""
+                   , (unsigned long)ae->when
+                   , (unsigned long)ae->duration
+                   , (unsigned long)ae->non_clear_duration
+                   , rrdcalc_status2string(ae->new_status)
+                   , rrdcalc_status2string(ae->old_status)
+                   , ae->delay
+                   , (unsigned long)ae->delay_up_to_timestamp
+                   , ae->updated_by_id
+                   , ae->updates_id
+                   , ae->new_value_string
+                   , ae->old_value_string
+    );
+
+    if(unlikely(ae->flags & HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION)) {
+        buffer_strcat(wb, "\t\t\"no_clear_notification\": true,\n");
+    }
+
+    buffer_strcat(wb, "\t\t\"value\":");
+    buffer_rrd_value(wb, ae->new_value);
+    buffer_strcat(wb, ",\n");
+
+    buffer_strcat(wb, "\t\t\"old_value\":");
+    buffer_rrd_value(wb, ae->old_value);
+    buffer_strcat(wb, "\n");
+
+    buffer_strcat(wb, "\t}");
+}
+
+void health_alarm_log2json(RRDHOST *host, BUFFER *wb, uint32_t after) {
+    pthread_rwlock_rdlock(&host->health_log.alarm_log_rwlock);
+
+    buffer_strcat(wb, "[");
+
+    unsigned int max = host->health_log.max;
+    unsigned int count = 0;
+    ALARM_ENTRY *ae;
+    for(ae = host->health_log.alarms; ae && count < max ; count++, ae = ae->next) {
+        if(ae->unique_id > after) {
+            if(likely(count)) buffer_strcat(wb, ",");
+            health_alarm_entry2json_nolock(wb, ae, host);
+        }
+    }
+
+    buffer_strcat(wb, "\n]\n");
+
+    pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock);
+}
+
+static inline void health_rrdcalc2json_nolock(RRDHOST *host, BUFFER *wb, RRDCALC *rc) {
+    char value_string[100 + 1];
+    format_value_and_unit(value_string, 100, rc->value, rc->units, -1);
+
+    buffer_sprintf(wb,
+            "\t\t\"%s.%s\": {\n"
+                    "\t\t\t\"id\": %lu,\n"
+                    "\t\t\t\"name\": \"%s\",\n"
+                    "\t\t\t\"chart\": \"%s\",\n"
+                    "\t\t\t\"family\": \"%s\",\n"
+                    "\t\t\t\"active\": %s,\n"
+                    "\t\t\t\"exec\": \"%s\",\n"
+                    "\t\t\t\"recipient\": \"%s\",\n"
+                    "\t\t\t\"source\": \"%s\",\n"
+                    "\t\t\t\"units\": \"%s\",\n"
+                    "\t\t\t\"info\": \"%s\",\n"
+                    "\t\t\t\"status\": \"%s\",\n"
+                    "\t\t\t\"last_status_change\": %lu,\n"
+                    "\t\t\t\"last_updated\": %lu,\n"
+                    "\t\t\t\"next_update\": %lu,\n"
+                    "\t\t\t\"update_every\": %d,\n"
+                    "\t\t\t\"delay_up_duration\": %d,\n"
+                    "\t\t\t\"delay_down_duration\": %d,\n"
+                    "\t\t\t\"delay_max_duration\": %d,\n"
+                    "\t\t\t\"delay_multiplier\": %f,\n"
+                    "\t\t\t\"delay\": %d,\n"
+                    "\t\t\t\"delay_up_to_timestamp\": %lu,\n"
+                    "\t\t\t\"value_string\": \"%s\",\n"
+                   , rc->chart, rc->name
+                   , (unsigned long)rc->id
+                   , rc->name
+                   , rc->chart
+                   , (rc->rrdset && rc->rrdset->family)?rc->rrdset->family:""
+                   , (rc->rrdset)?"true":"false"
+                   , rc->exec?rc->exec:host->health_default_exec
+                   , rc->recipient?rc->recipient:host->health_default_recipient
+                   , rc->source
+                   , rc->units?rc->units:""
+                   , rc->info?rc->info:""
+                   , rrdcalc_status2string(rc->status)
+                   , (unsigned long)rc->last_status_change
+                   , (unsigned long)rc->last_updated
+                   , (unsigned long)rc->next_update
+                   , rc->update_every
+                   , rc->delay_up_duration
+                   , rc->delay_down_duration
+                   , rc->delay_max_duration
+                   , rc->delay_multiplier
+                   , rc->delay_last
+                   , (unsigned long)rc->delay_up_to_timestamp
+                   , value_string
+    );
+
+    if(unlikely(rc->options & RRDCALC_FLAG_NO_CLEAR_NOTIFICATION)) {
+        buffer_strcat(wb, "\t\t\t\"no_clear_notification\": true,\n");
+    }
+
+    if(RRDCALC_HAS_DB_LOOKUP(rc)) {
+        if(rc->dimensions && *rc->dimensions)
+            health_string2json(wb, "\t\t\t", "lookup_dimensions", rc->dimensions, ",\n");
+
+        buffer_sprintf(wb,
+                "\t\t\t\"db_after\": %lu,\n"
+                        "\t\t\t\"db_before\": %lu,\n"
+                        "\t\t\t\"lookup_method\": \"%s\",\n"
+                        "\t\t\t\"lookup_after\": %d,\n"
+                        "\t\t\t\"lookup_before\": %d,\n"
+                        "\t\t\t\"lookup_options\": \"",
+                (unsigned long) rc->db_after,
+                (unsigned long) rc->db_before,
+                group_method2string(rc->group),
+                rc->after,
+                rc->before
+        );
+        buffer_data_options2string(wb, rc->options);
+        buffer_strcat(wb, "\",\n");
+    }
+
+    if(rc->calculation) {
+        health_string2json(wb, "\t\t\t", "calc", rc->calculation->source, ",\n");
+        health_string2json(wb, "\t\t\t", "calc_parsed", rc->calculation->parsed_as, ",\n");
+    }
+
+    if(rc->warning) {
+        health_string2json(wb, "\t\t\t", "warn", rc->warning->source, ",\n");
+        health_string2json(wb, "\t\t\t", "warn_parsed", rc->warning->parsed_as, ",\n");
+    }
+
+    if(rc->critical) {
+        health_string2json(wb, "\t\t\t", "crit", rc->critical->source, ",\n");
+        health_string2json(wb, "\t\t\t", "crit_parsed", rc->critical->parsed_as, ",\n");
+    }
+
+    buffer_strcat(wb, "\t\t\t\"green\":");
+    buffer_rrd_value(wb, rc->green);
+    buffer_strcat(wb, ",\n");
+
+    buffer_strcat(wb, "\t\t\t\"red\":");
+    buffer_rrd_value(wb, rc->red);
+    buffer_strcat(wb, ",\n");
+
+    buffer_strcat(wb, "\t\t\t\"value\":");
+    buffer_rrd_value(wb, rc->value);
+    buffer_strcat(wb, "\n");
+
+    buffer_strcat(wb, "\t\t}");
+}
+
+//void health_rrdcalctemplate2json_nolock(BUFFER *wb, RRDCALCTEMPLATE *rt) {
+//
+//}
+
+void health_alarms2json(RRDHOST *host, BUFFER *wb, int all) {
+    int i;
+
+    rrdhost_rdlock(host);
+    buffer_sprintf(wb, "{\n\t\"hostname\": \"%s\","
+                    "\n\t\"latest_alarm_log_unique_id\": %u,"
+                    "\n\t\"status\": %s,"
+                    "\n\t\"now\": %lu,"
+                    "\n\t\"alarms\": {\n",
+            host->hostname,
+            (host->health_log.next_log_id > 0)?(host->health_log.next_log_id - 1):0,
+            host->health_enabled?"true":"false",
+            (unsigned long)now_realtime_sec());
+
+    RRDCALC *rc;
+    for(i = 0, rc = host->alarms; rc ; rc = rc->next) {
+        if(unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec))
+            continue;
+
+        if(likely(!all && !(rc->status == RRDCALC_STATUS_WARNING || rc->status == RRDCALC_STATUS_CRITICAL)))
+            continue;
+
+        if(likely(i)) buffer_strcat(wb, ",\n");
+        health_rrdcalc2json_nolock(host, wb, rc);
+        i++;
+    }
+
+//    buffer_strcat(wb, "\n\t},\n\t\"templates\": {");
+//    RRDCALCTEMPLATE *rt;
+//    for(rt = host->templates; rt ; rt = rt->next)
+//        health_rrdcalctemplate2json_nolock(wb, rt);
+
+    buffer_strcat(wb, "\n\t}\n}\n");
+    rrdhost_unlock(host);
+}
+
+
+
diff --git a/src/health_log.c b/src/health_log.c
new file mode 100644 (file)
index 0000000..990894e
--- /dev/null
@@ -0,0 +1,465 @@
+#define NETDATA_HEALTH_INTERNALS
+#include "common.h"
+
+// ----------------------------------------------------------------------------
+// health alarm log load/save
+// no need for locking - only one thread is reading / writing the alarms log
+
+inline int health_alarm_log_open(RRDHOST *host) {
+    if(host->health_log_fp)
+        fclose(host->health_log_fp);
+
+    host->health_log_fp = fopen(host->health_log_filename, "a");
+
+    if(host->health_log_fp) {
+        if (setvbuf(host->health_log_fp, NULL, _IOLBF, 0) != 0)
+            error("HEALTH [%s]: cannot set line buffering on health log file '%s'.", host->hostname, host->health_log_filename);
+        return 0;
+    }
+
+    error("HEALTH [%s]: cannot open health log file '%s'. Health data will be lost in case of netdata or server crash.", host->hostname, host->health_log_filename);
+    return -1;
+}
+
+inline void health_alarm_log_close(RRDHOST *host) {
+    if(host->health_log_fp) {
+        fclose(host->health_log_fp);
+        host->health_log_fp = NULL;
+    }
+}
+
+inline void health_log_rotate(RRDHOST *host) {
+    static size_t rotate_every = 0;
+
+    if(unlikely(rotate_every == 0)) {
+        rotate_every = (size_t)config_get_number(CONFIG_SECTION_HEALTH, "rotate log every lines", 2000);
+        if(rotate_every < 100) rotate_every = 100;
+    }
+
+    if(unlikely(host->health_log_entries_written > rotate_every)) {
+        health_alarm_log_close(host);
+
+        char old_filename[FILENAME_MAX + 1];
+        snprintfz(old_filename, FILENAME_MAX, "%s.old", host->health_log_filename);
+
+        if(unlink(old_filename) == -1 && errno != ENOENT)
+            error("HEALTH [%s]: cannot remove old alarms log file '%s'", host->hostname, old_filename);
+
+        if(link(host->health_log_filename, old_filename) == -1 && errno != ENOENT)
+            error("HEALTH [%s]: cannot move file '%s' to '%s'.", host->hostname, host->health_log_filename, old_filename);
+
+        if(unlink(host->health_log_filename) == -1 && errno != ENOENT)
+            error("HEALTH [%s]: cannot remove old alarms log file '%s'", host->hostname, host->health_log_filename);
+
+        // open it with truncate
+        host->health_log_fp = fopen(host->health_log_filename, "w");
+
+        if(host->health_log_fp)
+            fclose(host->health_log_fp);
+        else
+            error("HEALTH [%s]: cannot truncate health log '%s'", host->hostname, host->health_log_filename);
+
+        host->health_log_fp = NULL;
+
+        host->health_log_entries_written = 0;
+        health_alarm_log_open(host);
+    }
+}
+
+inline void health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae) {
+    health_log_rotate(host);
+
+    if(likely(host->health_log_fp)) {
+        if(unlikely(fprintf(host->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 [%s]: failed to save alarm log entry to '%s'. Health data may be lost in case of abnormal restart.", host->hostname, host->health_log_filename);
+        else {
+            ae->flags |= HEALTH_ENTRY_FLAG_SAVED;
+            host->health_log_entries_written++;
+        }
+    }
+}
+
+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;
+
+    errno = 0;
+
+    char *s, *buf = mallocz(65536 + 1);
+    size_t line = 0, len = 0;
+    ssize_t loaded = 0, updated = 0, errored = 0, duplicate = 0;
+
+    pthread_rwlock_rdlock(&host->health_log.alarm_log_rwlock);
+
+    while((s = fgets_trim_len(buf, 65536, fp, &len))) {
+        host->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 [%s]: line %zu of file '%s' has more than %d entries. Ignoring excessive entries.", host->hostname, line, filename, max_entries);
+                    break;
+                }
+            }
+            else s++;
+        }
+
+        if(likely(*pointers[0] == 'U' || *pointers[0] == 'A')) {
+            ALARM_ENTRY *ae = NULL;
+
+            if(entries < 26) {
+                error("HEALTH [%s]: line %zu of file '%s' should have at least 26 entries, but it has %d. Ignoring it.", host->hostname, 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 [%s]: line %zu of file '%s' states alarm entry with invalid unique id %u (%s). Ignoring it.", host->hostname, line, filename, unique_id, pointers[2]);
+                errored++;
+                continue;
+            }
+
+            uint32_t alarm_id = (uint32_t)strtoul(pointers[3], NULL, 16);
+            if(!alarm_id) {
+                error("HEALTH [%s]: line %zu of file '%s' states alarm entry for invalid alarm id %u (%s). Ignoring it.", host->hostname, 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 [%s]: line %zu of file '%s' has alarm log entry %u in wrong order. Ignoring it.", host->hostname, 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 [%s]: line %zu of file '%s' adds duplicate alarm log entry %u. Using the later."
+                                  , host->hostname, 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(unlikely(!ae)) {
+                // error("HEALTH [%s]: line %zu of file '%s' updates alarm log entry with unique id %u, but it is not found.", host->hostname, line, filename, unique_id);
+                continue;
+            }
+
+            // check for a possible host missmatch
+            //if(strcmp(pointers[1], host->hostname))
+            //    error("HEALTH [%s]: line %zu of file '%s' provides an alarm for host '%s' but this is named '%s'.", host->hostname, 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);
+
+            freez(ae->name);
+            ae->name = strdupz(pointers[13]);
+            ae->hash_name = simple_hash(ae->name);
+
+            freez(ae->chart);
+            ae->chart = strdupz(pointers[14]);
+            ae->hash_chart = simple_hash(ae->chart);
+
+            freez(ae->family);
+            ae->family = strdupz(pointers[15]);
+
+            freez(ae->exec);
+            ae->exec = strdupz(pointers[16]);
+            if(!*ae->exec) { freez(ae->exec); ae->exec = NULL; }
+
+            freez(ae->recipient);
+            ae->recipient = strdupz(pointers[17]);
+            if(!*ae->recipient) { freez(ae->recipient); ae->recipient = NULL; }
+
+            freez(ae->source);
+            ae->source = strdupz(pointers[18]);
+            if(!*ae->source) { freez(ae->source); ae->source = NULL; }
+
+            freez(ae->units);
+            ae->units = strdupz(pointers[19]);
+            if(!*ae->units) { freez(ae->units); ae->units = NULL; }
+
+            freez(ae->info);
+            ae->info = strdupz(pointers[20]);
+            if(!*ae->info) { freez(ae->info); ae->info = NULL; }
+
+            ae->exec_code   = str2i(pointers[21]);
+            ae->new_status  = str2i(pointers[22]);
+            ae->old_status  = str2i(pointers[23]);
+            ae->delay       = str2i(pointers[24]);
+
+            ae->new_value   = str2l(pointers[25]);
+            ae->old_value   = str2l(pointers[26]);
+
+            static char value_string[100 + 1];
+            freez(ae->old_value_string);
+            freez(ae->new_value_string);
+            ae->old_value_string = strdupz(format_value_and_unit(value_string, 100, ae->old_value, ae->units, -1));
+            ae->new_value_string = strdupz(format_value_and_unit(value_string, 100, ae->new_value, ae->units, -1));
+
+            // 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 [%s]: line %zu of file '%s' is invalid (unrecognized entry type '%s').", host->hostname, 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)now_realtime_sec();
+    if(!max_alarm_id)  max_alarm_id  = (uint32_t)now_realtime_sec();
+
+    host->health_log.next_log_id = max_unique_id + 1;
+    host->health_log.next_alarm_id = max_alarm_id + 1;
+
+    debug(D_HEALTH, "HEALTH [%s]: loaded file '%s' with %zd new alarm entries, updated %zd alarms, errors %zd entries, duplicate %zd", host->hostname, filename, loaded, updated, errored, duplicate);
+    return loaded;
+}
+
+inline void health_alarm_log_load(RRDHOST *host) {
+    health_alarm_log_close(host);
+
+    char filename[FILENAME_MAX + 1];
+    snprintfz(filename, FILENAME_MAX, "%s.old", host->health_log_filename);
+    FILE *fp = fopen(filename, "r");
+    if(!fp)
+        error("HEALTH [%s]: cannot open health file: %s", host->hostname, filename);
+    else {
+        health_alarm_log_read(host, fp, filename);
+        fclose(fp);
+    }
+
+    host->health_log_entries_written = 0;
+    fp = fopen(host->health_log_filename, "r");
+    if(!fp)
+        error("HEALTH [%s]: cannot open health file: %s", host->hostname, host->health_log_filename);
+    else {
+        health_alarm_log_read(host, fp, host->health_log_filename);
+        fclose(fp);
+    }
+
+    health_alarm_log_open(host);
+}
+
+
+// ----------------------------------------------------------------------------
+// health alarm log management
+
+inline void health_alarm_log(
+        RRDHOST *host,
+        uint32_t alarm_id,
+        uint32_t alarm_event_id,
+        time_t when,
+        const char *name,
+        const char *chart,
+        const char *family,
+        const char *exec,
+        const char *recipient,
+        time_t duration,
+        calculated_number old_value,
+        calculated_number new_value,
+        int old_status,
+        int new_status,
+        const char *source,
+        const char *units,
+        const char *info,
+        int delay,
+        uint32_t flags
+) {
+    debug(D_HEALTH, "Health adding alarm log entry with id: %u", host->health_log.next_log_id);
+
+    ALARM_ENTRY *ae = callocz(1, sizeof(ALARM_ENTRY));
+    ae->name = strdupz(name);
+    ae->hash_name = simple_hash(ae->name);
+
+    if(chart) {
+        ae->chart = strdupz(chart);
+        ae->hash_chart = simple_hash(ae->chart);
+    }
+
+    if(family)
+        ae->family = strdupz(family);
+
+    if(exec) ae->exec = strdupz(exec);
+    if(recipient) ae->recipient = strdupz(recipient);
+    if(source) ae->source = strdupz(source);
+    if(units) ae->units = strdupz(units);
+    if(info) ae->info = strdupz(info);
+
+    ae->unique_id = host->health_log.next_log_id++;
+    ae->alarm_id = alarm_id;
+    ae->alarm_event_id = alarm_event_id;
+    ae->when = when;
+    ae->old_value = old_value;
+    ae->new_value = new_value;
+
+    static char value_string[100 + 1];
+    ae->old_value_string = strdupz(format_value_and_unit(value_string, 100, ae->old_value, ae->units, -1));
+    ae->new_value_string = strdupz(format_value_and_unit(value_string, 100, ae->new_value, ae->units, -1));
+
+    ae->old_status = old_status;
+    ae->new_status = new_status;
+    ae->duration = duration;
+    ae->delay = delay;
+    ae->delay_up_to_timestamp = when + delay;
+
+    ae->flags |= flags;
+
+    if(ae->old_status == RRDCALC_STATUS_WARNING || ae->old_status == RRDCALC_STATUS_CRITICAL)
+        ae->non_clear_duration += ae->duration;
+
+    // link it
+    pthread_rwlock_wrlock(&host->health_log.alarm_log_rwlock);
+    ae->next = host->health_log.alarms;
+    host->health_log.alarms = ae;
+    host->health_log.count++;
+    pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock);
+
+    // match previous alarms
+    pthread_rwlock_rdlock(&host->health_log.alarm_log_rwlock);
+    ALARM_ENTRY *t;
+    for(t = host->health_log.alarms ; t ; t = t->next) {
+        if(t != ae && t->alarm_id == ae->alarm_id) {
+            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;
+
+                if((t->new_status == RRDCALC_STATUS_WARNING || t->new_status == RRDCALC_STATUS_CRITICAL) &&
+                   (t->old_status == RRDCALC_STATUS_WARNING || t->old_status == RRDCALC_STATUS_CRITICAL))
+                    ae->non_clear_duration += t->non_clear_duration;
+
+                health_alarm_log_save(host, t);
+            }
+
+            // no need to continue
+            break;
+        }
+    }
+    pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock);
+
+    health_alarm_log_save(host, ae);
+}
+
+inline void health_alarm_log_free_one_nochecks_nounlink(ALARM_ENTRY *ae) {
+    freez(ae->name);
+    freez(ae->chart);
+    freez(ae->family);
+    freez(ae->exec);
+    freez(ae->recipient);
+    freez(ae->source);
+    freez(ae->units);
+    freez(ae->info);
+    freez(ae->old_value_string);
+    freez(ae->new_value_string);
+    freez(ae);
+}
+
+inline void health_alarm_log_free(RRDHOST *host) {
+    rrdhost_check_wrlock(host);
+
+    pthread_rwlock_wrlock(&host->health_log.alarm_log_rwlock);
+
+    ALARM_ENTRY *ae;
+    while((ae = host->health_log.alarms)) {
+        host->health_log.alarms = ae->next;
+        health_alarm_log_free_one_nochecks_nounlink(ae);
+    }
+
+    pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock);
+}
index 39797893a5b271ac94e7f40ce0cf0ef2d6245ece..1dabf5e192652ae5796acf71dd001c9027ecad54 100644 (file)
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -184,23 +184,27 @@ int do_ipc(int update_every, usec_t dt) {
             return 1;
         }
 
-        arrays_max     = rrdvar_custom_host_variable_create(&localhost, "ipc.semaphores.arrays.max");
-        semaphores_max = rrdvar_custom_host_variable_create(&localhost, "ipc.semaphores.max");
+        arrays_max     = rrdvar_custom_host_variable_create(localhost, "ipc.semaphores.arrays.max");
+        semaphores_max = rrdvar_custom_host_variable_create(localhost, "ipc.semaphores.max");
 
         if(arrays_max)     rrdvar_custom_host_variable_set(arrays_max, limits.semmni);
         if(semaphores_max) rrdvar_custom_host_variable_set(semaphores_max, limits.semmns);
 
         // create the charts
-        semaphores = rrdset_find("system.ipc_semaphores");
+        semaphores = rrdset_find_localhost("system.ipc_semaphores");
         if(!semaphores) {
-            semaphores = rrdset_create("system", "ipc_semaphores", NULL, "ipc semaphores", NULL, "IPC Semaphores", "semaphores", 1000, rrd_update_every, RRDSET_TYPE_AREA);
-            rrddim_add(semaphores, "semaphores", NULL, 1, 1, RRDDIM_ABSOLUTE);
+            semaphores = rrdset_create_localhost("system", "ipc_semaphores", NULL, "ipc semaphores", NULL
+                                                 , "IPC Semaphores", "semaphores", 1000, localhost->rrd_update_every
+                                                 , RRDSET_TYPE_AREA);
+            rrddim_add(semaphores, "semaphores", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
         }
 
-        arrays = rrdset_find("system.ipc_semaphore_arrays");
+        arrays = rrdset_find_localhost("system.ipc_semaphore_arrays");
         if(!arrays) {
-            arrays = rrdset_create("system", "ipc_semaphore_arrays", NULL, "ipc semaphores", NULL, "IPC Semaphore Arrays", "arrays", 1000, rrd_update_every, RRDSET_TYPE_AREA);
-            rrddim_add(arrays, "arrays", NULL, 1, 1, RRDDIM_ABSOLUTE);
+            arrays = rrdset_create_localhost("system", "ipc_semaphore_arrays", NULL, "ipc semaphores", NULL
+                                             , "IPC Semaphore Arrays", "arrays", 1000, localhost->rrd_update_every
+                                             , RRDSET_TYPE_AREA);
+            rrddim_add(arrays, "arrays", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
         }
     }
 
index d4c7fa14d904a6ee9007863e03f6ef73418ae0b9..855ecaee6ee71920c132792823ee783a28b7f015 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -1,7 +1,7 @@
 #include "common.h"
 
 const char *program_name = "";
-unsigned long long debug_flags = DEBUG;
+uint64_t debug_flags = DEBUG;
 
 int access_log_syslog = 1;
 int error_log_syslog = 1;
@@ -257,8 +257,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, "%s: INFO: (%04lu@%-10.10s:%-15.15s):", program_name, line, file, function);
-    else            fprintf(stderr, "%s: INFO: ", 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 );
 
index e61ffdd08ab596b2cc77d9638f7952a66d10abd1..24b971e2cf7c44a51e5434ecd81671e207ec05fd 100644 (file)
--- a/src/log.h
+++ b/src/log.h
@@ -1,38 +1,39 @@
 #ifndef NETDATA_LOG_H
 #define NETDATA_LOG_H 1
 
-#define D_WEB_BUFFER        0x00000001
-#define D_WEB_CLIENT        0x00000002
-#define D_LISTENER          0x00000004
-#define D_WEB_DATA          0x00000008
-#define D_OPTIONS           0x00000010
-#define D_PROCNETDEV_LOOP   0x00000020
-#define D_RRD_STATS         0x00000040
-#define D_WEB_CLIENT_ACCESS 0x00000080
-#define D_TC_LOOP           0x00000100
-#define D_DEFLATE           0x00000200
-#define D_CONFIG            0x00000400
-#define D_PLUGINSD          0x00000800
-#define D_CHILDS            0x00001000
-#define D_EXIT              0x00002000
-#define D_CHECKS            0x00004000
-#define D_NFACCT_LOOP       0x00008000
-#define D_PROCFILE          0x00010000
-#define D_RRD_CALLS         0x00020000
-#define D_DICTIONARY        0x00040000
-#define D_MEMORY            0x00080000
-#define D_CGROUP            0x00100000
-#define D_REGISTRY          0x00200000
-#define D_VARIABLES         0x00400000
-#define D_HEALTH            0x00800000
-#define D_CONNECT_TO        0x01000000
-#define D_SYSTEM            0x80000000
+#define D_WEB_BUFFER        0x0000000000000001
+#define D_WEB_CLIENT        0x0000000000000002
+#define D_LISTENER          0x0000000000000004
+#define D_WEB_DATA          0x0000000000000008
+#define D_OPTIONS           0x0000000000000010
+#define D_PROCNETDEV_LOOP   0x0000000000000020
+#define D_RRD_STATS         0x0000000000000040
+#define D_WEB_CLIENT_ACCESS 0x0000000000000080
+#define D_TC_LOOP           0x0000000000000100
+#define D_DEFLATE           0x0000000000000200
+#define D_CONFIG            0x0000000000000400
+#define D_PLUGINSD          0x0000000000000800
+#define D_CHILDS            0x0000000000001000
+#define D_EXIT              0x0000000000002000
+#define D_CHECKS            0x0000000000004000
+#define D_NFACCT_LOOP       0x0000000000008000
+#define D_PROCFILE          0x0000000000010000
+#define D_RRD_CALLS         0x0000000000020000
+#define D_DICTIONARY        0x0000000000040000
+#define D_MEMORY            0x0000000000080000
+#define D_CGROUP            0x0000000000100000
+#define D_REGISTRY          0x0000000000200000
+#define D_VARIABLES         0x0000000000400000
+#define D_HEALTH            0x0000000000800000
+#define D_CONNECT_TO        0x0000000001000000
+#define D_RRDHOST           0x0000000002000000
+#define D_SYSTEM            0x8000000000000000
 
 //#define DEBUG (D_WEB_CLIENT_ACCESS|D_LISTENER|D_RRD_STATS)
 //#define DEBUG 0xffffffff
 #define DEBUG (0)
 
-extern unsigned long long debug_flags;
+extern uint64_t debug_flags;
 
 extern const char *program_name;
 
index a62aa7a7e257f4a82192bf776ceb46f161bc0b7b..c47da52f1634461a81c74f8b4c7a736ae37013a8 100644 (file)
@@ -136,12 +136,14 @@ int do_macos_iokit(int update_every, usec_t dt) {
                         total_disk_writes += diskstat.bytes_write;
                     }
 
-                    st = rrdset_find_bytype("disk", diskstat.name);
+                    st = rrdset_find_bytype_localhost("disk", diskstat.name);
                     if (unlikely(!st)) {
-                        st = rrdset_create("disk", diskstat.name, NULL, diskstat.name, "disk.io", "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every, RRDSET_TYPE_AREA);
+                        st = rrdset_create_localhost("disk", diskstat.name, NULL, diskstat.name, "disk.io"
+                                                     , "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every
+                                                     , RRDSET_TYPE_AREA);
 
-                        rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_INCREMENTAL);
-                        rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_INCREMENTAL);
+                        rrddim_add(st, "reads", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
+                        rrddim_add(st, "writes", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL);
                     }
                     else rrdset_next(st);
 
@@ -161,13 +163,15 @@ int do_macos_iokit(int update_every, usec_t dt) {
                         CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.writes);
                     }
 
-                    st = rrdset_find_bytype("disk_ops", diskstat.name);
+                    st = rrdset_find_bytype_localhost("disk_ops", diskstat.name);
                     if (unlikely(!st)) {
-                        st = rrdset_create("disk_ops", diskstat.name, NULL, diskstat.name, "disk.ops", "Disk Completed I/O Operations", "operations/s", 2001, update_every, RRDSET_TYPE_LINE);
-                        st->isdetail = 1;
+                        st = rrdset_create_localhost("disk_ops", diskstat.name, NULL, diskstat.name, "disk.ops"
+                                                     , "Disk Completed I/O Operations", "operations/s", 2001
+                                                     , update_every, RRDSET_TYPE_LINE);
+                        rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                        rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                        rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                        rrddim_add(st, "reads", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                        rrddim_add(st, "writes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                     }
                     else rrdset_next(st);
 
@@ -187,12 +191,14 @@ int do_macos_iokit(int update_every, usec_t dt) {
                         CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.time_write);
                     }
 
-                    st = rrdset_find_bytype("disk_util", diskstat.name);
+                    st = rrdset_find_bytype_localhost("disk_util", diskstat.name);
                     if (unlikely(!st)) {
-                        st = rrdset_create("disk_util", diskstat.name, NULL, diskstat.name, "disk.util", "Disk Utilization Time", "% of time working", 2004, update_every, RRDSET_TYPE_AREA);
-                        st->isdetail = 1;
+                        st = rrdset_create_localhost("disk_util", diskstat.name, NULL, diskstat.name, "disk.util"
+                                                     , "Disk Utilization Time", "% of time working", 2004, update_every
+                                                     , RRDSET_TYPE_AREA);
+                        rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                        rrddim_add(st, "utilization", NULL, 1, 10000000, RRDDIM_INCREMENTAL);
+                        rrddim_add(st, "utilization", NULL, 1, 10000000, RRD_ALGORITHM_INCREMENTAL);
                     }
                     else rrdset_next(st);
 
@@ -212,13 +218,15 @@ int do_macos_iokit(int update_every, usec_t dt) {
                         CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.latency_write);
                     }
 
-                    st = rrdset_find_bytype("disk_iotime", diskstat.name);
+                    st = rrdset_find_bytype_localhost("disk_iotime", diskstat.name);
                     if (unlikely(!st)) {
-                        st = rrdset_create("disk_iotime", diskstat.name, NULL, diskstat.name, "disk.iotime", "Disk Total I/O Time", "milliseconds/s", 2022, update_every, RRDSET_TYPE_LINE);
-                        st->isdetail = 1;
+                        st = rrdset_create_localhost("disk_iotime", diskstat.name, NULL, diskstat.name, "disk.iotime"
+                                                     , "Disk Total I/O Time", "milliseconds/s", 2022, update_every
+                                                     , RRDSET_TYPE_LINE);
+                        rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                        rrddim_add(st, "reads", NULL, 1, 1000000, RRDDIM_INCREMENTAL);
-                        rrddim_add(st, "writes", NULL, -1, 1000000, RRDDIM_INCREMENTAL);
+                        rrddim_add(st, "reads", NULL, 1, 1000000, RRD_ALGORITHM_INCREMENTAL);
+                        rrddim_add(st, "writes", NULL, -1, 1000000, RRD_ALGORITHM_INCREMENTAL);
                     }
                     else rrdset_next(st);
 
@@ -236,13 +244,15 @@ int do_macos_iokit(int update_every, usec_t dt) {
 
                         // --------------------------------------------------------------------
 
-                        st = rrdset_find_bytype("disk_await", diskstat.name);
+                        st = rrdset_find_bytype_localhost("disk_await", diskstat.name);
                         if (unlikely(!st)) {
-                            st = rrdset_create("disk_await", diskstat.name, NULL, diskstat.name, "disk.await", "Average Completed I/O Operation Time", "ms per operation", 2005, update_every, RRDSET_TYPE_LINE);
-                            st->isdetail = 1;
+                            st = rrdset_create_localhost("disk_await", diskstat.name, NULL, diskstat.name, "disk.await"
+                                                         , "Average Completed I/O Operation Time", "ms per operation"
+                                                         , 2005, update_every, RRDSET_TYPE_LINE);
+                            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                            rrddim_add(st, "reads", NULL, 1, 1000000, RRDDIM_ABSOLUTE);
-                            rrddim_add(st, "writes", NULL, -1, 1000000, RRDDIM_ABSOLUTE);
+                            rrddim_add(st, "reads", NULL, 1, 1000000, RRD_ALGORITHM_ABSOLUTE);
+                            rrddim_add(st, "writes", NULL, -1, 1000000, RRD_ALGORITHM_ABSOLUTE);
                         }
                         else rrdset_next(st);
 
@@ -254,13 +264,16 @@ int do_macos_iokit(int update_every, usec_t dt) {
 
                         // --------------------------------------------------------------------
 
-                        st = rrdset_find_bytype("disk_avgsz", diskstat.name);
+                        st = rrdset_find_bytype_localhost("disk_avgsz", diskstat.name);
                         if (unlikely(!st)) {
-                            st = rrdset_create("disk_avgsz", diskstat.name, NULL, diskstat.name, "disk.avgsz", "Average Completed I/O Operation Bandwidth", "kilobytes per operation", 2006, update_every, RRDSET_TYPE_AREA);
-                            st->isdetail = 1;
-
-                            rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-                            rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_ABSOLUTE);
+                            st = rrdset_create_localhost("disk_avgsz", diskstat.name, NULL, diskstat.name, "disk.avgsz"
+                                                         , "Average Completed I/O Operation Bandwidth"
+                                                         , "kilobytes per operation", 2006, update_every
+                                                         , RRDSET_TYPE_AREA);
+                            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                            rrddim_add(st, "reads", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
+                            rrddim_add(st, "writes", NULL, -1, 1024, RRD_ALGORITHM_ABSOLUTE);
                         }
                         else rrdset_next(st);
 
@@ -272,12 +285,14 @@ int do_macos_iokit(int update_every, usec_t dt) {
 
                         // --------------------------------------------------------------------
 
-                        st = rrdset_find_bytype("disk_svctm", diskstat.name);
+                        st = rrdset_find_bytype_localhost("disk_svctm", diskstat.name);
                         if (unlikely(!st)) {
-                            st = rrdset_create("disk_svctm", diskstat.name, NULL, diskstat.name, "disk.svctm", "Average Service Time", "ms per operation", 2007, update_every, RRDSET_TYPE_LINE);
-                            st->isdetail = 1;
+                            st = rrdset_create_localhost("disk_svctm", diskstat.name, NULL, diskstat.name, "disk.svctm"
+                                                         , "Average Service Time", "ms per operation", 2007
+                                                         , update_every, RRDSET_TYPE_LINE);
+                            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                            rrddim_add(st, "svctm", NULL, 1, 1000000, RRDDIM_ABSOLUTE);
+                            rrddim_add(st, "svctm", NULL, 1, 1000000, RRD_ALGORITHM_ABSOLUTE);
                         }
                         else rrdset_next(st);
 
@@ -301,11 +316,12 @@ int do_macos_iokit(int update_every, usec_t dt) {
     }
 
     if (likely(do_io)) {
-        st = rrdset_find_bytype("system", "io");
+        st = rrdset_find_bytype_localhost("system", "io");
         if (unlikely(!st)) {
-            st = rrdset_create("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150, update_every, RRDSET_TYPE_AREA);
-            rrddim_add(st, "in",  NULL,  1, 1024, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "out", NULL, -1, 1024, RRDDIM_INCREMENTAL);
+            st = rrdset_create_localhost("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150
+                                         , update_every, RRDSET_TYPE_AREA);
+            rrddim_add(st, "in",  NULL,  1, 1024, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "out", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -340,17 +356,17 @@ int do_macos_iokit(int update_every, usec_t dt) {
                 // --------------------------------------------------------------------------
 
                 if (likely(do_space)) {
-                    st = rrdset_find_bytype("disk_space", mntbuf[i].f_mntonname);
+                    st = rrdset_find_bytype_localhost("disk_space", mntbuf[i].f_mntonname);
                     if (unlikely(!st)) {
                         snprintfz(title, 4096, "Disk Space Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname);
-                        st = rrdset_create("disk_space", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname, "disk.space", title, "GB", 2023,
-                                           update_every,
-                                           RRDSET_TYPE_STACKED);
+                        st = rrdset_create_localhost("disk_space", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname
+                                                     , "disk.space", title, "GB", 2023, update_every
+                                                     , RRDSET_TYPE_STACKED);
 
-                        rrddim_add(st, "avail", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRDDIM_ABSOLUTE);
-                        rrddim_add(st, "used", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRDDIM_ABSOLUTE);
+                        rrddim_add(st, "avail", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE);
+                        rrddim_add(st, "used", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE);
                         rrddim_add(st, "reserved_for_root", "reserved for root", mntbuf[i].f_bsize, GIGA_FACTOR,
-                                   RRDDIM_ABSOLUTE);
+                                   RRD_ALGORITHM_ABSOLUTE);
                     } else
                         rrdset_next(st);
 
@@ -363,15 +379,16 @@ int do_macos_iokit(int update_every, usec_t dt) {
                 // --------------------------------------------------------------------------
 
                 if (likely(do_inodes)) {
-                    st = rrdset_find_bytype("disk_inodes", mntbuf[i].f_mntonname);
+                    st = rrdset_find_bytype_localhost("disk_inodes", mntbuf[i].f_mntonname);
                     if (unlikely(!st)) {
                         snprintfz(title, 4096, "Disk Files (inodes) Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname);
-                        st = rrdset_create("disk_inodes", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname, "disk.inodes", title, "Inodes", 2024,
-                                           update_every, RRDSET_TYPE_STACKED);
+                        st = rrdset_create_localhost("disk_inodes", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname
+                                                     , "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);
+                        rrddim_add(st, "avail", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+                        rrddim_add(st, "used", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+                        rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1, RRD_ALGORITHM_ABSOLUTE);
                     } else
                         rrdset_next(st);
 
@@ -398,12 +415,13 @@ int do_macos_iokit(int update_every, usec_t dt) {
 
                 // --------------------------------------------------------------------
 
-                st = rrdset_find_bytype("net", ifa->ifa_name);
+                st = rrdset_find_bytype_localhost("net", ifa->ifa_name);
                 if (unlikely(!st)) {
-                    st = rrdset_create("net", ifa->ifa_name, NULL, ifa->ifa_name, "net.net", "Bandwidth", "kilobits/s", 7000, update_every, RRDSET_TYPE_AREA);
+                    st = rrdset_create_localhost("net", ifa->ifa_name, NULL, ifa->ifa_name, "net.net", "Bandwidth"
+                                                 , "kilobits/s", 7000, update_every, RRDSET_TYPE_AREA);
 
-                    rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "received", NULL, 8, 1024, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "sent", NULL, -8, 1024, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -413,15 +431,16 @@ int do_macos_iokit(int update_every, usec_t dt) {
 
                 // --------------------------------------------------------------------
 
-                st = rrdset_find_bytype("net_packets", ifa->ifa_name);
+                st = rrdset_find_bytype_localhost("net_packets", ifa->ifa_name);
                 if (unlikely(!st)) {
-                    st = rrdset_create("net_packets", ifa->ifa_name, NULL, ifa->ifa_name, "net.packets", "Packets", "packets/s", 7001, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
-
-                    rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "multicast_received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "multicast_sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    st = rrdset_create_localhost("net_packets", ifa->ifa_name, NULL, ifa->ifa_name, "net.packets"
+                                                 , "Packets", "packets/s", 7001, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                    rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "multicast_received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "multicast_sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -433,13 +452,14 @@ int do_macos_iokit(int update_every, usec_t dt) {
 
                 // --------------------------------------------------------------------
 
-                st = rrdset_find_bytype("net_errors", ifa->ifa_name);
+                st = rrdset_find_bytype_localhost("net_errors", ifa->ifa_name);
                 if (unlikely(!st)) {
-                    st = rrdset_create("net_errors", ifa->ifa_name, NULL, ifa->ifa_name, "net.errors", "Interface Errors", "errors/s", 7002, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("net_errors", ifa->ifa_name, NULL, ifa->ifa_name, "net.errors"
+                                                 , "Interface Errors", "errors/s", 7002, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "outbound", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -449,12 +469,13 @@ int do_macos_iokit(int update_every, usec_t dt) {
 
                 // --------------------------------------------------------------------
 
-                st = rrdset_find_bytype("net_drops", ifa->ifa_name);
+                st = rrdset_find_bytype_localhost("net_drops", ifa->ifa_name);
                 if (unlikely(!st)) {
-                    st = rrdset_create("net_drops", ifa->ifa_name, NULL, ifa->ifa_name, "net.drops", "Interface Drops", "drops/s", 7003, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("net_drops", ifa->ifa_name, NULL, ifa->ifa_name, "net.drops"
+                                                 , "Interface Drops", "drops/s", 7003, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -463,14 +484,16 @@ int do_macos_iokit(int update_every, usec_t dt) {
 
                 // --------------------------------------------------------------------
 
-                st = rrdset_find_bytype("net_events", ifa->ifa_name);
+                st = rrdset_find_bytype_localhost("net_events", ifa->ifa_name);
                 if (unlikely(!st)) {
-                    st = rrdset_create("net_events", ifa->ifa_name, NULL, ifa->ifa_name, "net.events", "Network Interface Events", "events/s", 7006, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
-
-                    rrddim_add(st, "frames", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "collisions", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "carrier", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    st = rrdset_create_localhost("net_events", ifa->ifa_name, NULL, ifa->ifa_name, "net.events"
+                                                 , "Network Interface Events", "events/s", 7006, update_every
+                                                 , RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                    rrddim_add(st, "frames", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "collisions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "carrier", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
index d86a032205bf798ed9d1048b6598087c0ae64cfb..da28255137be9741693a54a568e515eb4b3fa98d 100644 (file)
@@ -48,14 +48,15 @@ int do_macos_mach_smi(int update_every, usec_t dt) {
                 error("DISABLED: system.cpu");
             } else {
 
-                st = rrdset_find_bytype("system", "cpu");
+                st = rrdset_find_bytype_localhost("system", "cpu");
                 if (unlikely(!st)) {
-                    st = rrdset_create("system", "cpu", NULL, "cpu", "system.cpu", "Total CPU utilization", "percentage", 100, update_every, RRDSET_TYPE_STACKED);
+                    st = rrdset_create_localhost("system", "cpu", NULL, "cpu", "system.cpu", "Total CPU utilization"
+                                                 , "percentage", 100, update_every, RRDSET_TYPE_STACKED);
 
-                    rrddim_add(st, "user", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                    rrddim_add(st, "nice", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                    rrddim_add(st, "system", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                    rrddim_add(st, "idle", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "user", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "nice", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "system", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "idle", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
                     rrddim_hide(st, "idle");
                 }
                 else rrdset_next(st);
@@ -84,18 +85,19 @@ int do_macos_mach_smi(int update_every, usec_t dt) {
             error("DISABLED: mem.pgfaults");
         } else {
             if (likely(do_ram)) {
-                st = rrdset_find("system.ram");
+                st = rrdset_find_localhost("system.ram");
                 if (unlikely(!st)) {
-                    st = rrdset_create("system", "ram", NULL, "ram", NULL, "System RAM", "MB", 200, update_every, RRDSET_TYPE_STACKED);
-
-                    rrddim_add(st, "active",    NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE);
-                    rrddim_add(st, "wired",     NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE);
-                    rrddim_add(st, "throttled", NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE);
-                    rrddim_add(st, "compressor", NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE);
-                    rrddim_add(st, "inactive",  NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE);
-                    rrddim_add(st, "purgeable", NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE);
-                    rrddim_add(st, "speculative", NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE);
-                    rrddim_add(st, "free",      NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE);
+                    st = rrdset_create_localhost("system", "ram", NULL, "ram", NULL, "System RAM", "MB", 200
+                                                 , update_every, RRDSET_TYPE_STACKED);
+
+                    rrddim_add(st, "active",    NULL, system_pagesize, 1048576, RRD_ALGORITHM_ABSOLUTE);
+                    rrddim_add(st, "wired",     NULL, system_pagesize, 1048576, RRD_ALGORITHM_ABSOLUTE);
+                    rrddim_add(st, "throttled", NULL, system_pagesize, 1048576, RRD_ALGORITHM_ABSOLUTE);
+                    rrddim_add(st, "compressor", NULL, system_pagesize, 1048576, RRD_ALGORITHM_ABSOLUTE);
+                    rrddim_add(st, "inactive",  NULL, system_pagesize, 1048576, RRD_ALGORITHM_ABSOLUTE);
+                    rrddim_add(st, "purgeable", NULL, system_pagesize, 1048576, RRD_ALGORITHM_ABSOLUTE);
+                    rrddim_add(st, "speculative", NULL, system_pagesize, 1048576, RRD_ALGORITHM_ABSOLUTE);
+                    rrddim_add(st, "free",      NULL, system_pagesize, 1048576, RRD_ALGORITHM_ABSOLUTE);
                 }
                 else rrdset_next(st);
 
@@ -113,12 +115,13 @@ int do_macos_mach_smi(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if (likely(do_swapio)) {
-                st = rrdset_find("system.swapio");
+                st = rrdset_find_localhost("system.swapio");
                 if (unlikely(!st)) {
-                    st = rrdset_create("system", "swapio", NULL, "swap", NULL, "Swap I/O", "kilobytes/s", 250, update_every, RRDSET_TYPE_AREA);
+                    st = rrdset_create_localhost("system", "swapio", NULL, "swap", NULL, "Swap I/O", "kilobytes/s", 250
+                                                 , update_every, RRDSET_TYPE_AREA);
 
-                    rrddim_add(st, "in",  NULL, system_pagesize, 1024, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "out", NULL, -system_pagesize, 1024, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "in",  NULL, system_pagesize, 1024, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "out", NULL, -system_pagesize, 1024, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -130,20 +133,21 @@ int do_macos_mach_smi(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if (likely(do_pgfaults)) {
-                st = rrdset_find("mem.pgfaults");
+                st = rrdset_find_localhost("mem.pgfaults");
                 if (unlikely(!st)) {
-                    st = rrdset_create("mem", "pgfaults", NULL, "system", NULL, "Memory Page Faults", "page faults/s", 500, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
-
-                    rrddim_add(st, "memory",    NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "cow",       NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "pagein",    NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "pageout",   NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "compress",  NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "decompress", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "zero_fill", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "reactivate", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "purge",     NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    st = rrdset_create_localhost("mem", "pgfaults", NULL, "system", NULL, "Memory Page Faults"
+                                                 , "page faults/s", 500, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                    rrddim_add(st, "memory",    NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "cow",       NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "pagein",    NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "pageout",   NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "compress",  NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "decompress", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "zero_fill", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "reactivate", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "purge",     NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
index 1b8765cd858d2fe8bab774a1210a4eddcd1ab25f..f43789de20265c1c5dd80c07b26e2ce2057f1859 100644 (file)
@@ -41,10 +41,10 @@ int do_macos_sysctl(int update_every, usec_t dt) {
         do_tcp_packets          = config_get_boolean("plugin:macos:sysctl", "ipv4 TCP packets", 1);
         do_tcp_errors           = config_get_boolean("plugin:macos:sysctl", "ipv4 TCP errors", 1);
         do_tcp_handshake        = config_get_boolean("plugin:macos:sysctl", "ipv4 TCP handshake issues", 1);
-        do_ecn                  = config_get_boolean_ondemand("plugin:macos:sysctl", "ECN packets", CONFIG_ONDEMAND_ONDEMAND);
-        do_tcpext_syscookies    = config_get_boolean_ondemand("plugin:macos:sysctl", "TCP SYN cookies", CONFIG_ONDEMAND_ONDEMAND);
-        do_tcpext_ofo           = config_get_boolean_ondemand("plugin:macos:sysctl", "TCP out-of-order queue", CONFIG_ONDEMAND_ONDEMAND);
-        do_tcpext_connaborts    = config_get_boolean_ondemand("plugin:macos:sysctl", "TCP connection aborts", CONFIG_ONDEMAND_ONDEMAND);
+        do_ecn                  = config_get_boolean_ondemand("plugin:macos:sysctl", "ECN packets", CONFIG_BOOLEAN_AUTO);
+        do_tcpext_syscookies    = config_get_boolean_ondemand("plugin:macos:sysctl", "TCP SYN cookies", CONFIG_BOOLEAN_AUTO);
+        do_tcpext_ofo           = config_get_boolean_ondemand("plugin:macos:sysctl", "TCP out-of-order queue", CONFIG_BOOLEAN_AUTO);
+        do_tcpext_connaborts    = config_get_boolean_ondemand("plugin:macos:sysctl", "TCP connection aborts", CONFIG_BOOLEAN_AUTO);
         do_udp_packets          = config_get_boolean("plugin:macos:sysctl", "ipv4 UDP packets", 1);
         do_udp_errors           = config_get_boolean("plugin:macos:sysctl", "ipv4 UDP errors", 1);
         do_icmp_packets         = config_get_boolean("plugin:macos:sysctl", "ipv4 ICMP packets", 1);
@@ -53,17 +53,17 @@ int do_macos_sysctl(int update_every, usec_t dt) {
         do_ip_fragsout          = config_get_boolean("plugin:macos:sysctl", "ipv4 fragments sent", 1);
         do_ip_fragsin           = config_get_boolean("plugin:macos:sysctl", "ipv4 fragments assembly", 1);
         do_ip_errors            = config_get_boolean("plugin:macos:sysctl", "ipv4 errors", 1);
-        do_ip6_packets          = config_get_boolean_ondemand("plugin:macos:sysctl", "ipv6 packets", CONFIG_ONDEMAND_ONDEMAND);
-        do_ip6_fragsout         = config_get_boolean_ondemand("plugin:macos:sysctl", "ipv6 fragments sent", CONFIG_ONDEMAND_ONDEMAND);
-        do_ip6_fragsin          = config_get_boolean_ondemand("plugin:macos:sysctl", "ipv6 fragments assembly", CONFIG_ONDEMAND_ONDEMAND);
-        do_ip6_errors           = config_get_boolean_ondemand("plugin:macos:sysctl", "ipv6 errors", CONFIG_ONDEMAND_ONDEMAND);
-        do_icmp6                = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp", CONFIG_ONDEMAND_ONDEMAND);
-        do_icmp6_redir          = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp redirects", CONFIG_ONDEMAND_ONDEMAND);
-        do_icmp6_errors         = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp errors", CONFIG_ONDEMAND_ONDEMAND);
-        do_icmp6_echos          = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp echos", CONFIG_ONDEMAND_ONDEMAND);
-        do_icmp6_router         = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp router", CONFIG_ONDEMAND_ONDEMAND);
-        do_icmp6_neighbor       = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp neighbor", CONFIG_ONDEMAND_ONDEMAND);
-        do_icmp6_types          = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp types", CONFIG_ONDEMAND_ONDEMAND);
+        do_ip6_packets          = config_get_boolean_ondemand("plugin:macos:sysctl", "ipv6 packets", CONFIG_BOOLEAN_AUTO);
+        do_ip6_fragsout         = config_get_boolean_ondemand("plugin:macos:sysctl", "ipv6 fragments sent", CONFIG_BOOLEAN_AUTO);
+        do_ip6_fragsin          = config_get_boolean_ondemand("plugin:macos:sysctl", "ipv6 fragments assembly", CONFIG_BOOLEAN_AUTO);
+        do_ip6_errors           = config_get_boolean_ondemand("plugin:macos:sysctl", "ipv6 errors", CONFIG_BOOLEAN_AUTO);
+        do_icmp6                = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp", CONFIG_BOOLEAN_AUTO);
+        do_icmp6_redir          = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp redirects", CONFIG_BOOLEAN_AUTO);
+        do_icmp6_errors         = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp errors", CONFIG_BOOLEAN_AUTO);
+        do_icmp6_echos          = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp echos", CONFIG_BOOLEAN_AUTO);
+        do_icmp6_router         = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp router", CONFIG_BOOLEAN_AUTO);
+        do_icmp6_neighbor       = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp neighbor", CONFIG_BOOLEAN_AUTO);
+        do_icmp6_types          = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp types", CONFIG_BOOLEAN_AUTO);
         do_uptime               = config_get_boolean("plugin:macos:sysctl", "system uptime", 1);
     }
 
@@ -215,12 +215,14 @@ int do_macos_sysctl(int update_every, usec_t dt) {
                 error("DISABLED: system.load");
             } else {
 
-                st = rrdset_find_bytype("system", "load");
+                st = rrdset_find_bytype_localhost("system", "load");
                 if (unlikely(!st)) {
-                    st = rrdset_create("system", "load", NULL, "load", NULL, "System Load Average", "load", 100, (update_every < MIN_LOADAVG_UPDATE_EVERY) ? MIN_LOADAVG_UPDATE_EVERY : update_every, RRDSET_TYPE_LINE);
-                    rrddim_add(st, "load1", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-                    rrddim_add(st, "load5", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-                    rrddim_add(st, "load15", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+                    st = rrdset_create_localhost("system", "load", NULL, "load", NULL, "System Load Average", "load"
+                                                 , 100, (update_every < MIN_LOADAVG_UPDATE_EVERY)
+                                                        ? MIN_LOADAVG_UPDATE_EVERY : update_every, RRDSET_TYPE_LINE);
+                    rrddim_add(st, "load1", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
+                    rrddim_add(st, "load5", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
+                    rrddim_add(st, "load15", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
                 }
                 else rrdset_next(st);
 
@@ -242,13 +244,14 @@ int do_macos_sysctl(int update_every, usec_t dt) {
             do_swap = 0;
             error("DISABLED: system.swap");
         } else {
-            st = rrdset_find("system.swap");
+            st = rrdset_find_localhost("system.swap");
             if (unlikely(!st)) {
-                st = rrdset_create("system", "swap", NULL, "swap", NULL, "System Swap", "MB", 201, update_every, RRDSET_TYPE_STACKED);
-                st->isdetail = 1;
+                st = rrdset_create_localhost("system", "swap", NULL, "swap", NULL, "System Swap", "MB", 201
+                                             , update_every, RRDSET_TYPE_STACKED);
+                rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                rrddim_add(st, "free",    NULL, 1, 1048576, RRDDIM_ABSOLUTE);
-                rrddim_add(st, "used",    NULL, 1, 1048576, RRDDIM_ABSOLUTE);
+                rrddim_add(st, "free",    NULL, 1, 1048576, RRD_ALGORITHM_ABSOLUTE);
+                rrddim_add(st, "used",    NULL, 1, 1048576, RRD_ALGORITHM_ABSOLUTE);
             }
             else rrdset_next(st);
 
@@ -291,12 +294,13 @@ int do_macos_sysctl(int update_every, usec_t dt) {
                         iftot.ift_obytes += if2m->ifm_data.ifi_obytes;
                     }
                 }
-                st = rrdset_find("system.ipv4");
+                st = rrdset_find_localhost("system.ipv4");
                 if (unlikely(!st)) {
-                    st = rrdset_create("system", "ipv4", NULL, "network", NULL, "IPv4 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA);
+                    st = rrdset_create_localhost("system", "ipv4", NULL, "network", NULL, "IPv4 Bandwidth", "kilobits/s"
+                                                 , 500, update_every, RRDSET_TYPE_AREA);
 
-                    rrddim_add(st, "InOctets", "received", 8, 1024, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutOctets", "sent", -8, 1024, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InOctets", "received", 8, 1024, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutOctets", "sent", -8, 1024, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -328,14 +332,13 @@ int do_macos_sysctl(int update_every, usec_t dt) {
             error("DISABLED: ipv4.ecnpkts");
         } else {
             if (likely(do_tcp_packets)) {
-                st = rrdset_find("ipv4.tcppackets");
+                st = rrdset_find_localhost("ipv4.tcppackets");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "tcppackets", NULL, "tcp", NULL, "IPv4 TCP Packets",
-                                       "packets/s",
-                                       2600, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv4", "tcppackets", NULL, "tcp", NULL, "IPv4 TCP Packets"
+                                                 , "packets/s", 2600, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InSegs", "received", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutSegs", "sent", -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InSegs", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutSegs", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -347,16 +350,15 @@ int do_macos_sysctl(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if (likely(do_tcp_errors)) {
-                st = rrdset_find("ipv4.tcperrors");
+                st = rrdset_find_localhost("ipv4.tcperrors");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "tcperrors", NULL, "tcp", NULL, "IPv4 TCP Errors",
-                                       "packets/s",
-                                       2700, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
-
-                    rrddim_add(st, "InErrs", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "RetransSegs", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    st = rrdset_create_localhost("ipv4", "tcperrors", NULL, "tcp", NULL, "IPv4 TCP Errors", "packets/s"
+                                                 , 2700, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                    rrddim_add(st, "InErrs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "RetransSegs", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -369,17 +371,16 @@ int do_macos_sysctl(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if (likely(do_tcp_handshake)) {
-                st = rrdset_find("ipv4.tcphandshake");
+                st = rrdset_find_localhost("ipv4.tcphandshake");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "tcphandshake", NULL, "tcp", NULL,
-                                       "IPv4 TCP Handshake Issues",
-                                       "events/s", 2900, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
-
-                    rrddim_add(st, "EstabResets", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "ActiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "PassiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "AttemptFails", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    st = rrdset_create_localhost("ipv4", "tcphandshake", NULL, "tcp", NULL, "IPv4 TCP Handshake Issues"
+                                                 , "events/s", 2900, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                    rrddim_add(st, "EstabResets", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "ActiveOpens", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "PassiveOpens", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "AttemptFails", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -392,16 +393,17 @@ int do_macos_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_tcpext_connaborts == CONFIG_ONDEMAND_YES || (do_tcpext_connaborts == CONFIG_ONDEMAND_ONDEMAND && (tcpstat.tcps_rcvpackafterwin || tcpstat.tcps_rcvafterclose || tcpstat.tcps_rcvmemdrop || tcpstat.tcps_persistdrop))) {
-                do_tcpext_connaborts = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv4.tcpconnaborts");
+            if (do_tcpext_connaborts == CONFIG_BOOLEAN_YES || (do_tcpext_connaborts == CONFIG_BOOLEAN_AUTO && (tcpstat.tcps_rcvpackafterwin || tcpstat.tcps_rcvafterclose || tcpstat.tcps_rcvmemdrop || tcpstat.tcps_persistdrop))) {
+                do_tcpext_connaborts = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv4.tcpconnaborts");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "tcpconnaborts", NULL, "tcp", NULL, "TCP Connection Aborts", "connections/s", 3010, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv4", "tcpconnaborts", NULL, "tcp", NULL, "TCP Connection Aborts"
+                                                 , "connections/s", 3010, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "TCPAbortOnData",    "baddata",     1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "TCPAbortOnClose",   "userclosed",  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "TCPAbortOnMemory",  "nomemory",    1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "TCPAbortOnTimeout", "timeout",     1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "TCPAbortOnData",    "baddata",     1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "TCPAbortOnClose",   "userclosed",  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "TCPAbortOnMemory",  "nomemory",    1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "TCPAbortOnTimeout", "timeout",     1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -414,13 +416,14 @@ int do_macos_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_tcpext_ofo == CONFIG_ONDEMAND_YES || (do_tcpext_ofo == CONFIG_ONDEMAND_ONDEMAND && tcpstat.tcps_rcvoopack)) {
-                do_tcpext_ofo = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv4.tcpofo");
+            if (do_tcpext_ofo == CONFIG_BOOLEAN_YES || (do_tcpext_ofo == CONFIG_BOOLEAN_AUTO && tcpstat.tcps_rcvoopack)) {
+                do_tcpext_ofo = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv4.tcpofo");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "tcpofo", NULL, "tcp", NULL, "TCP Out-Of-Order Queue", "packets/s", 3050, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv4", "tcpofo", NULL, "tcp", NULL, "TCP Out-Of-Order Queue"
+                                                 , "packets/s", 3050, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "TCPOFOQueue", "inqueue",  1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "TCPOFOQueue", "inqueue",  1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -430,16 +433,17 @@ int do_macos_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_tcpext_syscookies == CONFIG_ONDEMAND_YES || (do_tcpext_syscookies == CONFIG_ONDEMAND_ONDEMAND && (tcpstat.tcps_sc_sendcookie || tcpstat.tcps_sc_recvcookie || tcpstat.tcps_sc_zonefail))) {
-                do_tcpext_syscookies = CONFIG_ONDEMAND_YES;
+            if (do_tcpext_syscookies == CONFIG_BOOLEAN_YES || (do_tcpext_syscookies == CONFIG_BOOLEAN_AUTO && (tcpstat.tcps_sc_sendcookie || tcpstat.tcps_sc_recvcookie || tcpstat.tcps_sc_zonefail))) {
+                do_tcpext_syscookies = CONFIG_BOOLEAN_YES;
 
-                st = rrdset_find("ipv4.tcpsyncookies");
+                st = rrdset_find_localhost("ipv4.tcpsyncookies");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "tcpsyncookies", NULL, "tcp", NULL, "TCP SYN Cookies", "packets/s", 3100, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv4", "tcpsyncookies", NULL, "tcp", NULL, "TCP SYN Cookies"
+                                                 , "packets/s", 3100, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "SyncookiesRecv",   "received",  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "SyncookiesSent",   "sent",     -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "SyncookiesFailed", "failed",   -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "SyncookiesRecv",   "received",  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "SyncookiesSent",   "sent",     -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "SyncookiesFailed", "failed",   -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -451,15 +455,16 @@ int do_macos_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_ecn == CONFIG_ONDEMAND_YES || (do_ecn == CONFIG_ONDEMAND_ONDEMAND && (tcpstat.tcps_ecn_recv_ce || tcpstat.tcps_ecn_not_supported))) {
-                do_ecn = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv4.ecnpkts");
+            if (do_ecn == CONFIG_BOOLEAN_YES || (do_ecn == CONFIG_BOOLEAN_AUTO && (tcpstat.tcps_ecn_recv_ce || tcpstat.tcps_ecn_not_supported))) {
+                do_ecn = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv4.ecnpkts");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "ecnpkts", NULL, "ecn", NULL, "IPv4 ECN Statistics", "packets/s", 8700, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("ipv4", "ecnpkts", NULL, "ecn", NULL, "IPv4 ECN Statistics"
+                                                 , "packets/s", 8700, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "InCEPkts", "CEP", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InNoECTPkts", "NoECTP", -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InCEPkts", "CEP", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InNoECTPkts", "NoECTP", -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -482,13 +487,13 @@ int do_macos_sysctl(int update_every, usec_t dt) {
             error("DISABLED: ipv4.udperrors");
         } else {
             if (likely(do_udp_packets)) {
-                st = rrdset_find("ipv4.udppackets");
+                st = rrdset_find_localhost("ipv4.udppackets");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "udppackets", NULL, "udp", NULL, "IPv4 UDP Packets",
-                                       "packets/s", 2601, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv4", "udppackets", NULL, "udp", NULL, "IPv4 UDP Packets"
+                                                 , "packets/s", 2601, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InDatagrams", "received", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InDatagrams", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -500,17 +505,17 @@ int do_macos_sysctl(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if (likely(do_udp_errors)) {
-                st = rrdset_find("ipv4.udperrors");
+                st = rrdset_find_localhost("ipv4.udperrors");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "udperrors", NULL, "udp", NULL, "IPv4 UDP Errors", "events/s",
-                                       2701, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
-
-                    rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "NoPorts", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    st = rrdset_create_localhost("ipv4", "udperrors", NULL, "udp", NULL, "IPv4 UDP Errors", "events/s"
+                                                 , 2701, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                    rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "NoPorts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -543,14 +548,13 @@ int do_macos_sysctl(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if (likely(do_icmp_packets)) {
-                st = rrdset_find("ipv4.icmp");
+                st = rrdset_find_localhost("ipv4.icmp");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "icmp", NULL, "icmp", NULL, "IPv4 ICMP Packets", "packets/s",
-                                       2602,
-                                       update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv4", "icmp", NULL, "icmp", NULL, "IPv4 ICMP Packets", "packets/s"
+                                                 , 2602, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InMsgs", "received", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutMsgs", "sent", -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InMsgs", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutMsgs", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -561,15 +565,14 @@ int do_macos_sysctl(int update_every, usec_t dt) {
 
                 // --------------------------------------------------------------------
 
-                st = rrdset_find("ipv4.icmp_errors");
+                st = rrdset_find_localhost("ipv4.icmp_errors");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "icmp_errors", NULL, "icmp", NULL, "IPv4 ICMP Errors",
-                                       "packets/s",
-                                       2603, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv4", "icmp_errors", NULL, "icmp", NULL, "IPv4 ICMP Errors"
+                                                 , "packets/s", 2603, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -583,15 +586,15 @@ int do_macos_sysctl(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if (likely(do_icmpmsg)) {
-                st = rrdset_find("ipv4.icmpmsg");
+                st = rrdset_find_localhost("ipv4.icmpmsg");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "icmpmsg", NULL, "icmp", NULL, "IPv4 ICMP Messsages",
-                                       "packets/s", 2604, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv4", "icmpmsg", NULL, "icmp", NULL, "IPv4 ICMP Messsages"
+                                                 , "packets/s", 2604, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InEchoReps", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutEchoReps", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InEchos", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutEchos", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InEchoReps", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutEchoReps", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InEchos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutEchos", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -620,15 +623,15 @@ int do_macos_sysctl(int update_every, usec_t dt) {
             error("DISABLED: ipv4.errors");
         } else {
             if (likely(do_ip_packets)) {
-                st = rrdset_find("ipv4.packets");
+                st = rrdset_find_localhost("ipv4.packets");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "packets", NULL, "packets", NULL, "IPv4 Packets", "packets/s",
-                                       3000, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv4", "packets", NULL, "packets", NULL, "IPv4 Packets", "packets/s"
+                                                 , 3000, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InReceives", "received", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutRequests", "sent", -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "ForwDatagrams", "forwarded", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InDelivers", "delivered", 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InReceives", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutRequests", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "ForwDatagrams", "forwarded", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InDelivers", "delivered", 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -642,15 +645,15 @@ int do_macos_sysctl(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if (likely(do_ip_fragsout)) {
-                st = rrdset_find("ipv4.fragsout");
+                st = rrdset_find_localhost("ipv4.fragsout");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "fragsout", NULL, "fragments", NULL, "IPv4 Fragments Sent",
-                                       "packets/s", 3010, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("ipv4", "fragsout", NULL, "fragments", NULL, "IPv4 Fragments Sent"
+                                                 , "packets/s", 3010, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "FragOKs", "ok", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "FragFails", "failed", -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "FragCreates", "created", 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "FragOKs", "ok", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "FragFails", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "FragCreates", "created", 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -663,16 +666,15 @@ int do_macos_sysctl(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if (likely(do_ip_fragsin)) {
-                st = rrdset_find("ipv4.fragsin");
+                st = rrdset_find_localhost("ipv4.fragsin");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "fragsin", NULL, "fragments", NULL,
-                                       "IPv4 Fragments Reassembly",
-                                       "packets/s", 3011, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
-
-                    rrddim_add(st, "ReasmOKs", "ok", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "ReasmFails", "failed", -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "ReasmReqds", "all", 1, 1, RRDDIM_INCREMENTAL);
+                    st = rrdset_create_localhost("ipv4", "fragsin", NULL, "fragments", NULL, "IPv4 Fragments Reassembly"
+                                                 , "packets/s", 3011, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                    rrddim_add(st, "ReasmOKs", "ok", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "ReasmFails", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "ReasmReqds", "all", 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -685,21 +687,20 @@ int do_macos_sysctl(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if (likely(do_ip_errors)) {
-                st = rrdset_find("ipv4.errors");
+                st = rrdset_find_localhost("ipv4.errors");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv4", "errors", NULL, "errors", NULL, "IPv4 Errors", "packets/s",
-                                       3002,
-                                       update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("ipv4", "errors", NULL, "errors", NULL, "IPv4 Errors", "packets/s"
+                                                 , 3002, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InDiscards", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutDiscards", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
 
-                    rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
 
-                    rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -727,19 +728,19 @@ int do_macos_sysctl(int update_every, usec_t dt) {
             do_ip6_errors = 0;
             error("DISABLED: ipv6.errors");
         } else {
-            if (do_ip6_packets == CONFIG_ONDEMAND_YES || (do_ip6_packets == CONFIG_ONDEMAND_ONDEMAND &&
+            if (do_ip6_packets == CONFIG_BOOLEAN_YES || (do_ip6_packets == CONFIG_BOOLEAN_AUTO &&
                                                           (ip6stat.ip6s_localout || ip6stat.ip6s_total ||
                                                            ip6stat.ip6s_forward || ip6stat.ip6s_delivered))) {
-                do_ip6_packets = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv6.packets");
+                do_ip6_packets = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv6.packets");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv6", "packets", NULL, "packets", NULL, "IPv6 Packets", "packets/s", 3000,
-                                       update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv6", "packets", NULL, "packets", NULL, "IPv6 Packets", "packets/s"
+                                                 , 3000, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "forwarded", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "delivers", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "forwarded", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "delivers", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -752,19 +753,19 @@ int do_macos_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_ip6_fragsout == CONFIG_ONDEMAND_YES || (do_ip6_fragsout == CONFIG_ONDEMAND_ONDEMAND &&
+            if (do_ip6_fragsout == CONFIG_BOOLEAN_YES || (do_ip6_fragsout == CONFIG_BOOLEAN_AUTO &&
                                                            (ip6stat.ip6s_fragmented || ip6stat.ip6s_cantfrag ||
                                                             ip6stat.ip6s_ofragments))) {
-                do_ip6_fragsout = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv6.fragsout");
+                do_ip6_fragsout = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv6.fragsout");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv6", "fragsout", NULL, "fragments", NULL, "IPv6 Fragments Sent",
-                                       "packets/s", 3010, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("ipv6", "fragsout", NULL, "fragments", NULL, "IPv6 Fragments Sent"
+                                                 , "packets/s", 3010, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "ok", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "failed", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "all", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -776,20 +777,20 @@ int do_macos_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_ip6_fragsin == CONFIG_ONDEMAND_YES || (do_ip6_fragsin == CONFIG_ONDEMAND_ONDEMAND &&
+            if (do_ip6_fragsin == CONFIG_BOOLEAN_YES || (do_ip6_fragsin == CONFIG_BOOLEAN_AUTO &&
                                                           (ip6stat.ip6s_reassembled || ip6stat.ip6s_fragdropped ||
                                                            ip6stat.ip6s_fragtimeout || ip6stat.ip6s_fragments))) {
-                do_ip6_fragsin = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv6.fragsin");
+                do_ip6_fragsin = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv6.fragsin");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv6", "fragsin", NULL, "fragments", NULL, "IPv6 Fragments Reassembly",
-                                       "packets/s", 3011, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
-
-                    rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "timeout", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    st = rrdset_create_localhost("ipv6", "fragsin", NULL, "fragments", NULL, "IPv6 Fragments Reassembly"
+                                                 , "packets/s", 3011, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                    rrddim_add(st, "ok", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "failed", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "timeout", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "all", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -802,7 +803,7 @@ int do_macos_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_ip6_errors == CONFIG_ONDEMAND_YES || (do_ip6_errors == CONFIG_ONDEMAND_ONDEMAND && (
+            if (do_ip6_errors == CONFIG_BOOLEAN_YES || (do_ip6_errors == CONFIG_BOOLEAN_AUTO && (
                     ip6stat.ip6s_toosmall ||
                     ip6stat.ip6s_odropped ||
                     ip6stat.ip6s_badoptions ||
@@ -812,22 +813,22 @@ int do_macos_sysctl(int update_every, usec_t dt) {
                     ip6stat.ip6s_tooshort ||
                     ip6stat.ip6s_cantforward ||
                     ip6stat.ip6s_noroute))) {
-                do_ip6_errors = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv6.errors");
+                do_ip6_errors = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv6.errors");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv6", "errors", NULL, "errors", NULL, "IPv6 Errors", "packets/s", 3002,
-                                       update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("ipv6", "errors", NULL, "errors", NULL, "IPv6 Errors", "packets/s"
+                                                 , 3002, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InDiscards", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutDiscards", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
 
-                    rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InTruncatedPkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InNoRoutes", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InTruncatedPkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InNoRoutes", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
 
-                    rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -858,15 +859,15 @@ int do_macos_sysctl(int update_every, usec_t dt) {
                 icmp6_total.msgs_out += icmp6stat.icp6s_outhist[i];
             }
             icmp6_total.msgs_in += icmp6stat.icp6s_badcode + icmp6stat.icp6s_badlen + icmp6stat.icp6s_checksum + icmp6stat.icp6s_tooshort;
-            if (do_icmp6 == CONFIG_ONDEMAND_YES || (do_icmp6 == CONFIG_ONDEMAND_ONDEMAND && (icmp6_total.msgs_in || icmp6_total.msgs_out))) {
-                do_icmp6 = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv6.icmp");
+            if (do_icmp6 == CONFIG_BOOLEAN_YES || (do_icmp6 == CONFIG_BOOLEAN_AUTO && (icmp6_total.msgs_in || icmp6_total.msgs_out))) {
+                do_icmp6 = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv6.icmp");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv6", "icmp", NULL, "icmp", NULL, "IPv6 ICMP Messages",
-                                       "messages/s", 10000, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv6", "icmp", NULL, "icmp", NULL, "IPv6 ICMP Messages", "messages/s"
+                                                 , 10000, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -877,15 +878,15 @@ int do_macos_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_icmp6_redir == CONFIG_ONDEMAND_YES || (do_icmp6_redir == CONFIG_ONDEMAND_ONDEMAND && (icmp6stat.icp6s_inhist[ND_REDIRECT] || icmp6stat.icp6s_outhist[ND_REDIRECT]))) {
-                do_icmp6_redir = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv6.icmpredir");
+            if (do_icmp6_redir == CONFIG_BOOLEAN_YES || (do_icmp6_redir == CONFIG_BOOLEAN_AUTO && (icmp6stat.icp6s_inhist[ND_REDIRECT] || icmp6stat.icp6s_outhist[ND_REDIRECT]))) {
+                do_icmp6_redir = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv6.icmpredir");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv6", "icmpredir", NULL, "icmp", NULL, "IPv6 ICMP Redirects",
-                                       "redirects/s", 10050, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv6", "icmpredir", NULL, "icmp", NULL, "IPv6 ICMP Redirects"
+                                                 , "redirects/s", 10050, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -896,7 +897,7 @@ int do_macos_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_icmp6_errors == CONFIG_ONDEMAND_YES || (do_icmp6_errors == CONFIG_ONDEMAND_ONDEMAND && (
+            if (do_icmp6_errors == CONFIG_BOOLEAN_YES || (do_icmp6_errors == CONFIG_BOOLEAN_AUTO && (
                                                                             icmp6stat.icp6s_badcode ||
                                                                             icmp6stat.icp6s_badlen ||
                                                                             icmp6stat.icp6s_checksum ||
@@ -908,22 +909,23 @@ int do_macos_sysctl(int update_every, usec_t dt) {
                                                                             icmp6stat.icp6s_outhist[ICMP6_DST_UNREACH] ||
                                                                             icmp6stat.icp6s_outhist[ICMP6_TIME_EXCEEDED] ||
                                                                             icmp6stat.icp6s_outhist[ICMP6_PARAM_PROB]))) {
-                do_icmp6_errors = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv6.icmperrors");
+                do_icmp6_errors = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv6.icmperrors");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv6", "icmperrors", NULL, "icmp", NULL, "IPv6 ICMP Errors", "errors/s", 10100, update_every, RRDSET_TYPE_LINE);
-
-                    rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
-
-                    rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InDestUnreachs", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InPktTooBigs", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InTimeExcds", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InParmProblems", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutDestUnreachs", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutTimeExcds", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutParmProblems", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    st = rrdset_create_localhost("ipv6", "icmperrors", NULL, "icmp", NULL, "IPv6 ICMP Errors"
+                                                 , "errors/s", 10100, update_every, RRDSET_TYPE_LINE);
+
+                    rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+
+                    rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InDestUnreachs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InPktTooBigs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InTimeExcds", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InParmProblems", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutDestUnreachs", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutTimeExcds", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutParmProblems", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -942,20 +944,21 @@ int do_macos_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_icmp6_echos == CONFIG_ONDEMAND_YES || (do_icmp6_echos == CONFIG_ONDEMAND_ONDEMAND && (
+            if (do_icmp6_echos == CONFIG_BOOLEAN_YES || (do_icmp6_echos == CONFIG_BOOLEAN_AUTO && (
                                                                  icmp6stat.icp6s_inhist[ICMP6_ECHO_REQUEST] ||
                                                                  icmp6stat.icp6s_outhist[ICMP6_ECHO_REQUEST] ||
                                                                  icmp6stat.icp6s_inhist[ICMP6_ECHO_REPLY] ||
                                                                  icmp6stat.icp6s_outhist[ICMP6_ECHO_REPLY]))) {
-                do_icmp6_echos = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv6.icmpechos");
+                do_icmp6_echos = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv6.icmpechos");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv6", "icmpechos", NULL, "icmp", NULL, "IPv6 ICMP Echo", "messages/s", 10200, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv6", "icmpechos", NULL, "icmp", NULL, "IPv6 ICMP Echo", "messages/s"
+                                                 , 10200, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InEchos", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutEchos", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InEchoReplies", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutEchoReplies", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InEchos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutEchos", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InEchoReplies", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutEchoReplies", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -968,20 +971,21 @@ int do_macos_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_icmp6_router == CONFIG_ONDEMAND_YES || (do_icmp6_router == CONFIG_ONDEMAND_ONDEMAND && (
+            if (do_icmp6_router == CONFIG_BOOLEAN_YES || (do_icmp6_router == CONFIG_BOOLEAN_AUTO && (
                                                                     icmp6stat.icp6s_inhist[ND_ROUTER_SOLICIT] ||
                                                                     icmp6stat.icp6s_outhist[ND_ROUTER_SOLICIT] ||
                                                                     icmp6stat.icp6s_inhist[ND_ROUTER_ADVERT] ||
                                                                     icmp6stat.icp6s_outhist[ND_ROUTER_ADVERT]))) {
-                do_icmp6_router = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv6.icmprouter");
+                do_icmp6_router = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv6.icmprouter");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv6", "icmprouter", NULL, "icmp", NULL, "IPv6 Router Messages", "messages/s", 10400, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv6", "icmprouter", NULL, "icmp", NULL, "IPv6 Router Messages"
+                                                 , "messages/s", 10400, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InSolicits", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutSolicits", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InSolicits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutSolicits", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -994,20 +998,21 @@ int do_macos_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_icmp6_neighbor == CONFIG_ONDEMAND_YES || (do_icmp6_neighbor == CONFIG_ONDEMAND_ONDEMAND && (
+            if (do_icmp6_neighbor == CONFIG_BOOLEAN_YES || (do_icmp6_neighbor == CONFIG_BOOLEAN_AUTO && (
                                                                     icmp6stat.icp6s_inhist[ND_NEIGHBOR_SOLICIT] ||
                                                                     icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT] ||
                                                                     icmp6stat.icp6s_inhist[ND_NEIGHBOR_ADVERT] ||
                                                                     icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]))) {
-                do_icmp6_neighbor = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv6.icmpneighbor");
+                do_icmp6_neighbor = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv6.icmpneighbor");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv6", "icmpneighbor", NULL, "icmp", NULL, "IPv6 Neighbor Messages", "messages/s", 10500, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv6", "icmpneighbor", NULL, "icmp", NULL, "IPv6 Neighbor Messages"
+                                                 , "messages/s", 10500, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InSolicits", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutSolicits", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InSolicits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutSolicits", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -1020,7 +1025,7 @@ int do_macos_sysctl(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if (do_icmp6_types == CONFIG_ONDEMAND_YES || (do_icmp6_types == CONFIG_ONDEMAND_ONDEMAND && (
+            if (do_icmp6_types == CONFIG_BOOLEAN_YES || (do_icmp6_types == CONFIG_BOOLEAN_AUTO && (
                                                                     icmp6stat.icp6s_inhist[1] ||
                                                                     icmp6stat.icp6s_inhist[128] ||
                                                                     icmp6stat.icp6s_inhist[129] ||
@@ -1031,22 +1036,22 @@ int do_macos_sysctl(int update_every, usec_t dt) {
                                                                     icmp6stat.icp6s_outhist[133] ||
                                                                     icmp6stat.icp6s_outhist[135] ||
                                                                     icmp6stat.icp6s_outhist[136]))) {
-                do_icmp6_types = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv6.icmptypes");
+                do_icmp6_types = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv6.icmptypes");
                 if (unlikely(!st)) {
-                    st = rrdset_create("ipv6", "icmptypes", NULL, "icmp", NULL, "IPv6 ICMP Types",
-                                       "messages/s", 10700, update_every, RRDSET_TYPE_LINE);
-
-                    rrddim_add(st, "InType1", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InType128", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InType129", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InType136", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutType1", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutType128", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutType129", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutType133", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutType135", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutType143", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    st = rrdset_create_localhost("ipv6", "icmptypes", NULL, "icmp", NULL, "IPv6 ICMP Types"
+                                                 , "messages/s", 10700, update_every, RRDSET_TYPE_LINE);
+
+                    rrddim_add(st, "InType1", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InType128", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InType129", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InType136", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutType1", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutType128", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutType129", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutType133", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutType135", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutType143", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 } else
                     rrdset_next(st);
 
@@ -1073,11 +1078,12 @@ int do_macos_sysctl(int update_every, usec_t dt) {
             error("DISABLED: system.uptime");
         } else {
             clock_gettime(CLOCK_REALTIME, &cur_time);
-            st = rrdset_find("system.uptime");
+            st = rrdset_find_localhost("system.uptime");
 
             if(unlikely(!st)) {
-                st = rrdset_create("system", "uptime", NULL, "uptime", NULL, "System Uptime", "seconds", 1000, update_every, RRDSET_TYPE_LINE);
-                rrddim_add(st, "uptime", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                st = rrdset_create_localhost("system", "uptime", NULL, "uptime", NULL, "System Uptime", "seconds", 1000
+                                             , update_every, RRDSET_TYPE_LINE);
+                rrddim_add(st, "uptime", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
             }
             else rrdset_next(st);
 
index 278e0aafd827c968539300e619c7ba3ab3956f85..86bd8604be39ba7b6687ed584f4ab88760e5e01c 100644 (file)
@@ -10,7 +10,7 @@ void netdata_cleanup_and_exit(int ret) {
     debug(D_EXIT, "Called: netdata_cleanup_and_exit()");
 
     // save the database
-    rrdset_save_all();
+    rrdhost_save_all();
 
     // unlink the pid
     if(pidfile[0]) {
@@ -23,7 +23,8 @@ void netdata_cleanup_and_exit(int ret) {
     //kill_childs();
 
     // free database
-    rrdset_free_all();
+    sleep(2);
+    rrdhost_free_all();
 #endif
 
     info("netdata exiting. Bye bye...");
@@ -34,51 +35,55 @@ struct netdata_static_thread static_threads[] = {
 #ifdef INTERNAL_PLUGIN_NFACCT
 // nfacct requires root access
     // so, we build it as an external plugin with setuid to root
-    {"nfacct",              "plugins",  "nfacct",     1, NULL, NULL, nfacct_main},
+    {"nfacct",              CONFIG_SECTION_PLUGINS,  "nfacct",     1, NULL, NULL, nfacct_main},
 #endif
 
-    {"tc",                 "plugins",   "tc",         1, NULL, NULL, tc_main},
-    {"idlejitter",         "plugins",   "idlejitter", 1, NULL, NULL, cpuidlejitter_main},
+    {"tc",                  CONFIG_SECTION_PLUGINS,  "tc",         1, NULL, NULL, tc_main},
+    {"idlejitter",          CONFIG_SECTION_PLUGINS,  "idlejitter", 1, NULL, NULL, cpuidlejitter_main},
 #if defined(__FreeBSD__)
-    {"freebsd",            "plugins",   "freebsd",    1, NULL, NULL, freebsd_main},
+    {"freebsd",             CONFIG_SECTION_PLUGINS,  "freebsd",    1, NULL, NULL, freebsd_main},
 #elif defined(__APPLE__)
-    {"macos",              "plugins",   "macos",      1, NULL, NULL, macos_main},
+    {"macos",               CONFIG_SECTION_PLUGINS,  "macos",      1, NULL, NULL, macos_main},
 #else
-    {"proc",               "plugins",   "proc",       1, NULL, NULL, proc_main},
-    {"diskspace",          "plugins",   "diskspace",  1, NULL, NULL, proc_diskspace_main},
-    {"cgroups",            "plugins",   "cgroups",    1, NULL, NULL, cgroups_main},
+    {"proc",                CONFIG_SECTION_PLUGINS,  "proc",       1, NULL, NULL, proc_main},
+    {"diskspace",           CONFIG_SECTION_PLUGINS,  "diskspace",  1, NULL, NULL, proc_diskspace_main},
+    {"cgroups",             CONFIG_SECTION_PLUGINS,  "cgroups",    1, NULL, NULL, cgroups_main},
 #endif /* __FreeBSD__, __APPLE__*/
-    {"check",              "plugins",   "checks",     0, NULL, NULL, checks_main},
-    {"backends",            NULL,       NULL,         1, NULL, NULL, backends_main},
-    {"health",              NULL,       NULL,         1, NULL, NULL, health_main},
-    {"plugins.d",           NULL,       NULL,         1, NULL, NULL, pluginsd_main},
-    {"web",                 NULL,       NULL,         1, NULL, NULL, socket_listen_main_multi_threaded},
-    {"web-single-threaded", NULL,       NULL,         0, NULL, NULL, socket_listen_main_single_threaded},
-    {NULL,                  NULL,       NULL,         0, NULL, NULL, NULL}
+    {"check",               CONFIG_SECTION_PLUGINS,  "checks",     0, NULL, NULL, checks_main},
+    {"backends",            NULL,                    NULL,         1, NULL, NULL, backends_main},
+    {"health",              NULL,                    NULL,         1, NULL, NULL, health_main},
+    {"plugins.d",           NULL,                    NULL,         1, NULL, NULL, pluginsd_main},
+    {"web",                 NULL,                    NULL,         1, NULL, NULL, socket_listen_main_multi_threaded},
+    {"web-single-threaded", NULL,                    NULL,         0, NULL, NULL, socket_listen_main_single_threaded},
+    {"push-metrics",        NULL,                    NULL,         0, NULL, NULL, rrdpush_sender_thread},
+    {NULL,                  NULL,                    NULL,         0, NULL, NULL, NULL}
 };
 
 void web_server_threading_selection(void) {
-    int threaded = config_get_boolean("global", "multi threaded web server", 1);
+    web_server_mode = web_server_mode_id(config_get(CONFIG_SECTION_WEB, "mode", web_server_mode_name(web_server_mode)));
+
+    int multi_threaded = (web_server_mode == WEB_SERVER_MODE_MULTI_THREADED);
+    int single_threaded = (web_server_mode == WEB_SERVER_MODE_SINGLE_THREADED);
 
     int i;
     for(i = 0; static_threads[i].name ; i++) {
         if(static_threads[i].start_routine == socket_listen_main_multi_threaded)
-            static_threads[i].enabled = threaded?1:0;
+            static_threads[i].enabled = multi_threaded;
 
         if(static_threads[i].start_routine == socket_listen_main_single_threaded)
-            static_threads[i].enabled = threaded?0:1;
+            static_threads[i].enabled = single_threaded;
     }
 
-    web_client_timeout = (int) config_get_number("global", "disconnect idle web clients after seconds", DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS);
+    web_client_timeout = (int) config_get_number(CONFIG_SECTION_WEB, "disconnect idle clients after seconds", DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS);
 
-    respect_web_browser_do_not_track_policy = config_get_boolean("global", "respect web browser do not track policy", respect_web_browser_do_not_track_policy);
-    web_x_frame_options = config_get("global", "web x-frame-options header", "");
+    respect_web_browser_do_not_track_policy = config_get_boolean(CONFIG_SECTION_WEB, "respect do not track policy", respect_web_browser_do_not_track_policy);
+    web_x_frame_options = config_get(CONFIG_SECTION_WEB, "x-frame-options response header", "");
     if(!*web_x_frame_options) web_x_frame_options = NULL;
 
 #ifdef NETDATA_WITH_ZLIB
-    web_enable_gzip = config_get_boolean("global", "enable web responses gzip compression", web_enable_gzip);
+    web_enable_gzip = config_get_boolean(CONFIG_SECTION_WEB, "enable gzip compression", web_enable_gzip);
 
-    char *s = config_get("global", "web compression strategy", "default");
+    char *s = config_get(CONFIG_SECTION_WEB, "gzip compression strategy", "default");
     if(!strcmp(s, "default"))
         web_gzip_strategy = Z_DEFAULT_STRATEGY;
     else if(!strcmp(s, "filtered"))
@@ -94,7 +99,7 @@ void web_server_threading_selection(void) {
         web_gzip_strategy = Z_DEFAULT_STRATEGY;
     }
 
-    web_gzip_level = (int)config_get_number("global", "web compression level", 3);
+    web_gzip_level = (int)config_get_number(CONFIG_SECTION_WEB, "gzip compression level", 3);
     if(web_gzip_level < 1) {
         error("Invalid compression level %d. Valid levels are 1 (fastest) to 9 (best ratio). Proceeding with level 1 (fastest compression).", web_gzip_level);
         web_gzip_level = 1;
@@ -220,7 +225,6 @@ struct option_def options[] = {
     { 'h', "Display this help message.",                  NULL,          NULL},
     { 'P', "File to save a pid while running.",           "filename",    "do not save pid to a file"},
     { 'i', "The IP address to listen to.",                "IP",          "all IP addresses IPv4 and IPv6"},
-    { 'k', "Check health configuration and exit.",        NULL,          NULL},
     { 'p', "API/Web port to use.",                        "port",        "19999"},
     { 's', "Prefix for /proc and /sys (for containers).", "path",        "no prefix"},
     { 't', "The internal clock of netdata.",              "seconds",     "1"},
@@ -334,9 +338,188 @@ static const char *verify_required_directory(const char *dir) {
     return dir;
 }
 
+void log_init(void) {
+    char filename[FILENAME_MAX + 1];
+    snprintfz(filename, FILENAME_MAX, "%s/debug.log", netdata_configured_log_dir);
+    stdout_filename    = config_get(CONFIG_SECTION_GLOBAL, "debug log",  filename);
+
+    snprintfz(filename, FILENAME_MAX, "%s/error.log", netdata_configured_log_dir);
+    stderr_filename    = config_get(CONFIG_SECTION_GLOBAL, "error log",  filename);
+
+    snprintfz(filename, FILENAME_MAX, "%s/access.log", netdata_configured_log_dir);
+    stdaccess_filename = config_get(CONFIG_SECTION_GLOBAL, "access log", filename);
+
+    error_log_throttle_period_backup =
+    error_log_throttle_period = config_get_number(CONFIG_SECTION_GLOBAL, "errors flood protection period", error_log_throttle_period);
+    error_log_errors_per_period = (unsigned long)config_get_number(CONFIG_SECTION_GLOBAL, "errors to trigger flood protection", (long long int)error_log_errors_per_period);
+
+    setenv("NETDATA_ERRORS_THROTTLE_PERIOD", config_get(CONFIG_SECTION_GLOBAL, "errors flood protection period"    , ""), 1);
+    setenv("NETDATA_ERRORS_PER_PERIOD",      config_get(CONFIG_SECTION_GLOBAL, "errors to trigger flood protection", ""), 1);
+}
+
+static void backwards_compatible_config() {
+    // allow existing configurations to work with the current version of netdata
+
+    if(config_exists(CONFIG_SECTION_GLOBAL, "multi threaded web server")) {
+        int mode = config_get_boolean(CONFIG_SECTION_GLOBAL, "multi threaded web server", 1);
+        web_server_mode = (mode)?WEB_SERVER_MODE_MULTI_THREADED:WEB_SERVER_MODE_SINGLE_THREADED;
+    }
+
+    // move [global] options to the [api] section
+    config_move(CONFIG_SECTION_GLOBAL, "bind socket to IP",
+                CONFIG_SECTION_WEB,    "bind to");
+
+    config_move(CONFIG_SECTION_GLOBAL, "bind to",
+                CONFIG_SECTION_WEB,    "bind to");
+
+    config_move(CONFIG_SECTION_GLOBAL, "port",
+                CONFIG_SECTION_WEB,    "default port");
+
+    config_move(CONFIG_SECTION_GLOBAL, "default port",
+                CONFIG_SECTION_WEB,    "default port");
+
+    config_move(CONFIG_SECTION_GLOBAL, "disconnect idle web clients after seconds",
+                CONFIG_SECTION_WEB,    "disconnect idle clients after seconds");
+
+    config_move(CONFIG_SECTION_GLOBAL, "respect web browser do not track policy",
+                CONFIG_SECTION_WEB,    "respect do not track policy");
+
+    config_move(CONFIG_SECTION_GLOBAL, "web x-frame-options header",
+                CONFIG_SECTION_WEB,    "x-frame-options response header");
+
+    config_move(CONFIG_SECTION_GLOBAL, "enable web responses gzip compression",
+                CONFIG_SECTION_WEB,    "enable gzip compression");
+
+    config_move(CONFIG_SECTION_GLOBAL, "web compression strategy",
+                CONFIG_SECTION_WEB,    "gzip compression strategy");
+
+    config_move(CONFIG_SECTION_GLOBAL, "web compression level",
+                CONFIG_SECTION_WEB,    "gzip compression level");
+
+    config_move(CONFIG_SECTION_GLOBAL, "web files owner",
+                CONFIG_SECTION_WEB,    "web files owner");
+
+    config_move(CONFIG_SECTION_GLOBAL, "web files group",
+                CONFIG_SECTION_WEB,    "web files group");
+}
+
+static void get_netdata_configured_variables() {
+    backwards_compatible_config();
+
+    // ------------------------------------------------------------------------
+    // get the hostname
+
+    char buf[HOSTNAME_MAX + 1];
+    if(gethostname(buf, HOSTNAME_MAX) == -1)
+        error("WARNING: Cannot get machine hostname.");
+
+    netdata_configured_hostname = config_get(CONFIG_SECTION_GLOBAL, "hostname", buf);
+    debug(D_OPTIONS, "hostname set to '%s'", netdata_configured_hostname);
+
+    netdata_configured_hostname    = config_get(CONFIG_SECTION_GLOBAL, "hostname",    CONFIG_DIR);
+
+    // ------------------------------------------------------------------------
+    // get default database size
+
+    default_rrd_history_entries = (int) config_get_number(CONFIG_SECTION_GLOBAL, "history", align_entries_to_pagesize(default_rrd_memory_mode, RRD_DEFAULT_HISTORY_ENTRIES));
+
+    long h = align_entries_to_pagesize(default_rrd_memory_mode, default_rrd_history_entries);
+    if(h != default_rrd_history_entries) {
+        config_set_number(CONFIG_SECTION_GLOBAL, "history", h);
+        default_rrd_history_entries = (int)h;
+    }
+
+    if(default_rrd_history_entries < 5 || default_rrd_history_entries > RRD_HISTORY_ENTRIES_MAX) {
+        error("Invalid history entries %d given. Defaulting to %d.", default_rrd_history_entries, RRD_DEFAULT_HISTORY_ENTRIES);
+        default_rrd_history_entries = RRD_DEFAULT_HISTORY_ENTRIES;
+    }
+
+    // ------------------------------------------------------------------------
+    // get default database update frequency
+
+    default_rrd_update_every = (int) config_get_number(CONFIG_SECTION_GLOBAL, "update every", UPDATE_EVERY);
+    if(default_rrd_update_every < 1 || default_rrd_update_every > 600) {
+        error("Invalid data collection frequency (update every) %d given. Defaulting to %d.", default_rrd_update_every, UPDATE_EVERY_MAX);
+        default_rrd_update_every = UPDATE_EVERY;
+    }
+
+    // ------------------------------------------------------------------------
+    // let the plugins know the min update_every
+
+    // get system paths
+    netdata_configured_config_dir  = config_get(CONFIG_SECTION_GLOBAL, "config directory",    CONFIG_DIR);
+    netdata_configured_log_dir     = config_get(CONFIG_SECTION_GLOBAL, "log directory",       LOG_DIR);
+    netdata_configured_plugins_dir = config_get(CONFIG_SECTION_GLOBAL, "plugins directory",   PLUGINS_DIR);
+    netdata_configured_web_dir     = config_get(CONFIG_SECTION_GLOBAL, "web files directory", WEB_DIR);
+    netdata_configured_cache_dir   = config_get(CONFIG_SECTION_GLOBAL, "cache directory",     CACHE_DIR);
+    netdata_configured_varlib_dir  = config_get(CONFIG_SECTION_GLOBAL, "lib directory",       VARLIB_DIR);
+    netdata_configured_home_dir    = config_get(CONFIG_SECTION_GLOBAL, "home directory",      CACHE_DIR);
+
+    // ------------------------------------------------------------------------
+    // get default memory mode for the database
+
+    default_rrd_memory_mode = rrd_memory_mode_id(config_get(CONFIG_SECTION_GLOBAL, "memory mode", rrd_memory_mode_name(default_rrd_memory_mode)));
+
+    // ------------------------------------------------------------------------
+
+    netdata_configured_host_prefix = config_get(CONFIG_SECTION_GLOBAL, "host access prefix", "");
+
+    // --------------------------------------------------------------------
+    // get KSM settings
+
+#ifdef MADV_MERGEABLE
+    enable_ksm = config_get_boolean(CONFIG_SECTION_GLOBAL, "memory deduplication (ksm)", enable_ksm);
+#endif
+
+    // --------------------------------------------------------------------
+    // get various system parameters
+
+    get_system_HZ();
+    get_system_cpus();
+    get_system_pid_max();
+}
+
+void set_global_environment() {
+    {
+        char b[16];
+        snprintfz(b, 15, "%d", default_rrd_update_every);
+        setenv("NETDATA_UPDATE_EVERY", b, 1);
+    }
+
+    setenv("NETDATA_HOSTNAME"   , netdata_configured_hostname, 1);
+    setenv("NETDATA_CONFIG_DIR" , verify_required_directory(netdata_configured_config_dir),  1);
+    setenv("NETDATA_PLUGINS_DIR", verify_required_directory(netdata_configured_plugins_dir), 1);
+    setenv("NETDATA_WEB_DIR"    , verify_required_directory(netdata_configured_web_dir),     1);
+    setenv("NETDATA_CACHE_DIR"  , verify_required_directory(netdata_configured_cache_dir),   1);
+    setenv("NETDATA_LIB_DIR"    , verify_required_directory(netdata_configured_varlib_dir),  1);
+    setenv("NETDATA_LOG_DIR"    , verify_required_directory(netdata_configured_log_dir),     1);
+    setenv("HOME"               , verify_required_directory(netdata_configured_home_dir),    1);
+    setenv("NETDATA_HOST_PREFIX", netdata_configured_host_prefix, 1);
+
+    // avoid flood calls to stat(/etc/localtime)
+    // http://stackoverflow.com/questions/4554271/how-to-avoid-excessive-stat-etc-localtime-calls-in-strftime-on-linux
+    setenv("TZ", ":/etc/localtime", 0);
+
+    // set the path we need
+    char path[1024 + 1], *p = getenv("PATH");
+    if(!p) p = "/bin:/usr/bin";
+    snprintfz(path, 1024, "%s:%s", p, "/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin");
+    setenv("PATH", config_get(CONFIG_SECTION_PLUGINS, "PATH environment variable", path), 1);
+
+    // python options
+    p = getenv("PYTHONPATH");
+    if(!p) p = "";
+    setenv("PYTHONPATH", config_get(CONFIG_SECTION_PLUGINS, "PYTHONPATH environment variable", p), 1);
+
+    // disable buffering for python plugins
+    setenv("PYTHONUNBUFFERED", "1", 1);
+
+    // switch to standard locale for plugins
+    setenv("LC_ALL", "C", 1);
+}
+
 int main(int argc, char **argv) {
-    char *hostname = "localhost";
-    int i, check_config = 0;
+    int i;
     int config_loaded = 0;
     int dont_fork = 0;
     size_t wanted_stacksize = 0, stacksize = 0;
@@ -361,12 +544,12 @@ int main(int argc, char **argv) {
                 remove_option(i, &argc, argv);
             }
             else if(strcmp(argv[i], "-ch") == 0 && (i+1) < argc) {
-                config_set("global", "host access prefix", argv[i+1]);
+                config_set(CONFIG_SECTION_GLOBAL, "host access prefix", argv[i+1]);
                 fprintf(stderr, "%s: deprecated option -- %s -- please use -s instead.\n", argv[0], argv[i]);
                 remove_option(i, &argc, argv);
             }
             else if(strcmp(argv[i], "-l") == 0 && (i+1) < argc) {
-                config_set("global", "history", argv[i+1]);
+                config_set(CONFIG_SECTION_GLOBAL, "history", argv[i+1]);
                 fprintf(stderr, "%s: deprecated option -- %s -- This option will be removed with V2.*.\n", argv[0], argv[i]);
                 remove_option(i, &argc, argv);
             }
@@ -396,7 +579,7 @@ int main(int argc, char **argv) {
         while( (opt = getopt(argc, argv, optstring)) != -1 ) {
             switch(opt) {
                 case 'c':
-                    if(load_config(optarg, 1) != 1) {
+                    if(config_load(optarg, 1) != 1) {
                         error("Cannot load configuration file %s.", optarg);
                         exit(1);
                     }
@@ -412,27 +595,23 @@ int main(int argc, char **argv) {
                     help(0);
                     break;
                 case 'i':
-                    config_set("global", "bind to", optarg);
-                    break;
-                case 'k':
-                    dont_fork = 1;
-                    check_config = 1;
+                    config_set(CONFIG_SECTION_WEB, "bind to", optarg);
                     break;
                 case 'P':
                     strncpy(pidfile, optarg, FILENAME_MAX);
                     pidfile[FILENAME_MAX] = '\0';
                     break;
                 case 'p':
-                    config_set("global", "default port", optarg);
+                    config_set(CONFIG_SECTION_GLOBAL, "default port", optarg);
                     break;
                 case 's':
-                    config_set("global", "host access prefix", optarg);
+                    config_set(CONFIG_SECTION_GLOBAL, "host access prefix", optarg);
                     break;
                 case 't':
-                    config_set("global", "update every", optarg);
+                    config_set(CONFIG_SECTION_GLOBAL, "update every", optarg);
                     break;
                 case 'u':
-                    config_set("global", "run as user", optarg);
+                    config_set(CONFIG_SECTION_GLOBAL, "run as user", optarg);
                     break;
                 case 'v':
                     printf("%s %s\n", program_name, program_version);
@@ -442,7 +621,15 @@ int main(int argc, char **argv) {
                         char* stacksize_string = "stacksize=";
                         char* debug_flags_string = "debug_flags=";
                         if(strcmp(optarg, "unittest") == 0) {
-                            rrd_update_every = 1;
+                            default_rrd_update_every = 1;
+                            default_rrd_memory_mode = RRD_MEMORY_MODE_RAM;
+                            if(!config_loaded) config_load(NULL, 0);
+                            get_netdata_configured_variables();
+                            default_rrd_update_every = 1;
+                            default_rrd_memory_mode = RRD_MEMORY_MODE_RAM;
+                            default_health_enabled = 0;
+                            rrd_init("unittest");
+                            default_rrdpush_enabled = 0;
                             if(run_all_mockup_tests()) exit(1);
                             if(unit_test_storage()) exit(1);
                             fprintf(stderr, "\n\nALL TESTS PASSED\n\n");
@@ -492,11 +679,11 @@ int main(int argc, char **argv) {
                         }
                         else if(strncmp(optarg, stacksize_string, strlen(stacksize_string)) == 0) {
                             optarg += strlen(stacksize_string);
-                            config_set("global", "pthread stack size", optarg);
+                            config_set(CONFIG_SECTION_GLOBAL, "pthread stack size", optarg);
                         }
                         else if(strncmp(optarg, debug_flags_string, strlen(debug_flags_string)) == 0) {
                             optarg += strlen(debug_flags_string);
-                            config_set("global", "debug flags",  optarg);
+                            config_set(CONFIG_SECTION_GLOBAL, "debug flags",  optarg);
                             debug_flags = strtoull(optarg, NULL, 0);
                         }
                     }
@@ -519,70 +706,44 @@ int main(int argc, char **argv) {
 #endif
 
     if(!config_loaded)
-        load_config(NULL, 0);
+        config_load(NULL, 0);
 
+    // ------------------------------------------------------------------------
+    // initialize netdata
     {
-        char *pmax = config_get("global", "glibc malloc arena max for plugins", "1");
+        char *pmax = config_get(CONFIG_SECTION_GLOBAL, "glibc malloc arena max for plugins", "1");
         if(pmax && *pmax)
             setenv("MALLOC_ARENA_MAX", pmax, 1);
 
 #if defined(HAVE_C_MALLOPT)
-        int i = config_get_number("global", "glibc malloc arena max for netdata", 1);
+        i = (int)config_get_number(CONFIG_SECTION_GLOBAL, "glibc malloc arena max for netdata", 1);
         if(i > 0)
             mallopt(M_ARENA_MAX, 1);
 #endif
 
         // prepare configuration environment variables for the plugins
 
-        netdata_configured_config_dir  = config_get("global", "config directory",    CONFIG_DIR);
-        netdata_configured_log_dir     = config_get("global", "log directory",       LOG_DIR);
-        netdata_configured_plugins_dir = config_get("global", "plugins directory",   PLUGINS_DIR);
-        netdata_configured_web_dir     = config_get("global", "web files directory", WEB_DIR);
-        netdata_configured_cache_dir   = config_get("global", "cache directory",     CACHE_DIR);
-        netdata_configured_varlib_dir  = config_get("global", "lib directory",       VARLIB_DIR);
-        netdata_configured_home_dir    = config_get("global", "home directory",      CACHE_DIR);
-
-        setenv("NETDATA_CONFIG_DIR" , verify_required_directory(netdata_configured_config_dir),  1);
-        setenv("NETDATA_PLUGINS_DIR", verify_required_directory(netdata_configured_plugins_dir), 1);
-        setenv("NETDATA_WEB_DIR"    , verify_required_directory(netdata_configured_web_dir),     1);
-        setenv("NETDATA_CACHE_DIR"  , verify_required_directory(netdata_configured_cache_dir),   1);
-        setenv("NETDATA_LIB_DIR"    , verify_required_directory(netdata_configured_varlib_dir),  1);
-        setenv("NETDATA_LOG_DIR"    , verify_required_directory(netdata_configured_log_dir),     1);
-        setenv("HOME"               , verify_required_directory(netdata_configured_home_dir),    1);
-
-        netdata_configured_host_prefix = config_get("global", "host access prefix", "");
-        setenv("NETDATA_HOST_PREFIX", netdata_configured_host_prefix, 1);
-
-        // disable buffering for python plugins
-        setenv("PYTHONUNBUFFERED", "1", 1);
-
-        // avoid flood calls to stat(/etc/localtime)
-        // http://stackoverflow.com/questions/4554271/how-to-avoid-excessive-stat-etc-localtime-calls-in-strftime-on-linux
-        setenv("TZ", ":/etc/localtime", 0);
+        get_netdata_configured_variables();
+        set_global_environment();
 
         // work while we are cd into config_dir
         // to allow the plugins refer to their config
         // files using relative filenames
         if(chdir(netdata_configured_config_dir) == -1)
             fatal("Cannot cd to '%s'", netdata_configured_config_dir);
-
-        char path[1024 + 1], *p = getenv("PATH");
-        if(!p) p = "/bin:/usr/bin";
-        snprintfz(path, 1024, "%s:%s", p, "/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin");
-        setenv("PATH", config_get("plugins", "PATH environment variable", path), 1);
-
-        p = getenv("PYTHONPATH");
-        if(!p) p = "";
-        setenv("PYTHONPATH", config_get("plugins", "PYTHONPATH environment variable", p), 1);
     }
 
     char *user = NULL;
+
     {
-        char *flags = config_get("global", "debug flags",  "0x00000000");
+        // --------------------------------------------------------------------
+        // get the debugging flags from the configuration file
+
+        char *flags = config_get(CONFIG_SECTION_GLOBAL, "debug flags",  "0x0000000000000000");
         setenv("NETDATA_DEBUG_FLAGS", flags, 1);
 
         debug_flags = strtoull(flags, NULL, 0);
-        debug(D_OPTIONS, "Debug flags set to '0x%8llx'.", debug_flags);
+        debug(D_OPTIONS, "Debug flags set to '0x%" PRIX64 "'.", debug_flags);
 
         if(debug_flags != 0) {
             struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
@@ -594,91 +755,25 @@ int main(int argc, char **argv) {
 #endif
         }
 
-        // --------------------------------------------------------------------
-
-#ifdef MADV_MERGEABLE
-        enable_ksm = config_get_boolean("global", "memory deduplication (ksm)", enable_ksm);
-#else
-#warning "Kernel memory deduplication (KSM) is not available"
-#endif
 
         // --------------------------------------------------------------------
+        // get log filenames and settings
 
-        get_system_HZ();
-        get_system_cpus();
-        get_system_pid_max();
-        
-        // --------------------------------------------------------------------
-
-        {
-            char filename[FILENAME_MAX + 1];
-            snprintfz(filename, FILENAME_MAX, "%s/debug.log", netdata_configured_log_dir);
-            stdout_filename    = config_get("global", "debug log",  filename);
-
-            snprintfz(filename, FILENAME_MAX, "%s/error.log", netdata_configured_log_dir);
-            stderr_filename    = config_get("global", "error log",  filename);
-
-            snprintfz(filename, FILENAME_MAX, "%s/access.log", netdata_configured_log_dir);
-            stdaccess_filename = config_get("global", "access log", filename);
-        }
-
-        error_log_throttle_period_backup =
-            error_log_throttle_period = config_get_number("global", "errors flood protection period", error_log_throttle_period);
-        setenv("NETDATA_ERRORS_THROTTLE_PERIOD", config_get("global", "errors flood protection period"    , ""), 1);
-
-        error_log_errors_per_period = (unsigned long)config_get_number("global", "errors to trigger flood protection", error_log_errors_per_period);
-        setenv("NETDATA_ERRORS_PER_PERIOD"     , config_get("global", "errors to trigger flood protection", ""), 1);
-
-        if(check_config) {
-            stdout_filename = stderr_filename = stdaccess_filename = "system";
-            error_log_throttle_period = 0;
-            error_log_errors_per_period = 0;
-        }
+        log_init();
         error_log_limit_unlimited();
 
-        // --------------------------------------------------------------------
-
-        rrd_memory_mode = rrd_memory_mode_id(config_get("global", "memory mode", rrd_memory_mode_name(rrd_memory_mode)));
 
         // --------------------------------------------------------------------
-
+        // load stream.conf
         {
-            char hostnamebuf[HOSTNAME_MAX + 1];
-            if(gethostname(hostnamebuf, HOSTNAME_MAX) == -1)
-                error("WARNING: Cannot get machine hostname.");
-            hostname = config_get("global", "hostname", hostnamebuf);
-            debug(D_OPTIONS, "hostname set to '%s'", hostname);
-            setenv("NETDATA_HOSTNAME", hostname, 1);
-        }
-
-        // --------------------------------------------------------------------
-
-        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) {
-            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 {
-            debug(D_OPTIONS, "save lines set to %d.", rrd_default_history_entries);
-        }
-
-        // --------------------------------------------------------------------
-
-        rrd_update_every = (int) config_get_number("global", "update every", UPDATE_EVERY);
-        if(rrd_update_every < 1 || rrd_update_every > 600) {
-            error("Invalid data collection frequency (update every) %d given. Defaulting to %d.", rrd_update_every, UPDATE_EVERY_MAX);
-            rrd_update_every = UPDATE_EVERY;
+            char filename[FILENAME_MAX + 1];
+            snprintfz(filename, FILENAME_MAX, "%s/stream.conf", netdata_configured_config_dir);
+            appconfig_load(&stream_config, filename, 0);
         }
-        else debug(D_OPTIONS, "update timer set to %d.", rrd_update_every);
 
-        // let the plugins know the min update_every
-        {
-            char buf[16];
-            snprintfz(buf, 15, "%d", rrd_update_every);
-            setenv("NETDATA_UPDATE_EVERY", buf, 1);
-        }
 
         // --------------------------------------------------------------------
+        // setup process signals
 
         // block signals while initializing threads.
         // this causes the threads to block signals.
@@ -724,7 +819,9 @@ int main(int argc, char **argv) {
         if(sigaction(SIGUSR2, &sa, NULL) == -1)
             error("Failed to change signal handler for SIGUSR2");
 
+
         // --------------------------------------------------------------------
+        // get the required stack size of the threads of netdata
 
         i = pthread_attr_init(&attr);
         if(i != 0)
@@ -736,30 +833,44 @@ int main(int argc, char **argv) {
         else
             debug(D_OPTIONS, "initial pthread stack size is %zu bytes", stacksize);
 
-        wanted_stacksize = (size_t)config_get_number("global", "pthread stack size", (long)stacksize);
+        wanted_stacksize = (size_t)config_get_number(CONFIG_SECTION_GLOBAL, "pthread stack size", (long)stacksize);
+
 
         // --------------------------------------------------------------------
+        // check which threads are enabled and initialize them
 
         for (i = 0; static_threads[i].name != NULL ; i++) {
             struct netdata_static_thread *st = &static_threads[i];
 
-            if(st->config_name) st->enabled = config_get_boolean(st->config_section, st->config_name, st->enabled);
-            if(st->enabled && st->init_routine) st->init_routine();
+            if(st->config_name)
+                st->enabled = config_get_boolean(st->config_section, st->config_name, st->enabled);
+
+            if(st->enabled && st->init_routine)
+                st->init_routine();
         }
 
-        // --------------------------------------------------------------------
 
+        // --------------------------------------------------------------------
         // get the user we should run
+
         // IMPORTANT: this is required before web_files_uid()
-        user = config_get("global", "run as user"    , (getuid() == 0)?NETDATA_USER:"");
+        if(getuid() == 0) {
+            user = config_get(CONFIG_SECTION_GLOBAL, "run as user", NETDATA_USER);
+        }
+        else {
+            struct passwd *passwd = getpwuid(getuid());
+            user = config_get(CONFIG_SECTION_GLOBAL, "run as user", (passwd && passwd->pw_name)?passwd->pw_name:"");
+        }
 
         // IMPORTANT: these have to run once, while single threaded
         web_files_uid(); // IMPORTANT: web_files_uid() before web_files_gid()
         web_files_gid();
 
+
         // --------------------------------------------------------------------
+        // create the listening sockets
 
-        if(!check_config)
+        if(web_server_mode != WEB_SERVER_MODE_NONE)
             create_listen_sockets();
     }
 
@@ -777,14 +888,16 @@ int main(int argc, char **argv) {
     }
 #endif /* NETDATA_INTERNAL_CHECKS */
 
+
     // fork, switch user, create pid file, set process priority
     if(become_daemon(dont_fork, user) == -1)
         fatal("Cannot daemonize myself.");
 
     info("netdata started on pid %d.", getpid());
 
+
     // ------------------------------------------------------------------------
-    // get default pthread stack size
+    // set default pthread stack size - after we have forked
 
     if(stacksize < wanted_stacksize) {
         i = pthread_attr_setstacksize(&attr, wanted_stacksize);
@@ -794,29 +907,19 @@ int main(int argc, char **argv) {
             debug(D_SYSTEM, "Successfully set pthread stacksize to %zu bytes", wanted_stacksize);
     }
 
-    // ------------------------------------------------------------------------
-    // initialize rrd host
-
-    rrdhost_init(hostname);
 
     // ------------------------------------------------------------------------
-    // initialize the registry
+    // initialize rrd, registry, health, rrdpush, etc.
 
-    registry_init();
-
-    // ------------------------------------------------------------------------
-    // initialize health monitoring
+    rrd_init(netdata_configured_hostname);
 
-    health_init();
-
-    if(check_config)
-        exit(1);
 
     // ------------------------------------------------------------------------
     // enable log flood protection
 
     error_log_limit_reset();
 
+
     // ------------------------------------------------------------------------
     // spawn the threads
 
@@ -841,6 +944,7 @@ int main(int argc, char **argv) {
 
     info("netdata initialization completed. Enjoy real-time performance monitoring!");
 
+
     // ------------------------------------------------------------------------
     // block signals while initializing threads.
     sigset_t sigset;
index fcc542e689ead4f61c79d722170cfbf2c75a7c25..3a0a83bda278f9b0f9b6c07d7c9d140a510ea673 100644 (file)
@@ -11,23 +11,26 @@ void *checks_main(void *ptr) {
     if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
         error("Cannot set pthread cancel state to ENABLE.");
 
-    usec_t usec = 0, susec = rrd_update_every * USEC_PER_SEC, loop_usec = 0, total_susec = 0;
+    usec_t usec = 0, susec = localhost->rrd_update_every * USEC_PER_SEC, loop_usec = 0, total_susec = 0;
     struct timeval now, last, loop;
 
     RRDSET *check1, *check2, *check3, *apps_cpu = NULL;
 
-    check1 = rrdset_create("netdata", "check1", NULL, "netdata", NULL, "Caller gives microseconds", "a million !", 99999, rrd_update_every, RRDSET_TYPE_LINE);
-    rrddim_add(check1, "absolute", NULL, -1, 1, RRDDIM_ABSOLUTE);
-    rrddim_add(check1, "incremental", NULL, 1, 1, RRDDIM_INCREMENTAL);
+    check1 = rrdset_create_localhost("netdata", "check1", NULL, "netdata", NULL, "Caller gives microseconds"
+                                     , "a million !", 99999, localhost->rrd_update_every, RRDSET_TYPE_LINE);
+    rrddim_add(check1, "absolute", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE);
+    rrddim_add(check1, "incremental", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
 
-    check2 = rrdset_create("netdata", "check2", NULL, "netdata", NULL, "Netdata calcs microseconds", "a million !", 99999, rrd_update_every, RRDSET_TYPE_LINE);
-    rrddim_add(check2, "absolute", NULL, -1, 1, RRDDIM_ABSOLUTE);
-    rrddim_add(check2, "incremental", NULL, 1, 1, RRDDIM_INCREMENTAL);
+    check2 = rrdset_create_localhost("netdata", "check2", NULL, "netdata", NULL, "Netdata calcs microseconds"
+                                     , "a million !", 99999, localhost->rrd_update_every, RRDSET_TYPE_LINE);
+    rrddim_add(check2, "absolute", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE);
+    rrddim_add(check2, "incremental", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
 
-    check3 = rrdset_create("netdata", "checkdt", NULL, "netdata", NULL, "Clock difference", "microseconds diff", 99999, rrd_update_every, RRDSET_TYPE_LINE);
-    rrddim_add(check3, "caller", NULL, 1, 1, RRDDIM_ABSOLUTE);
-    rrddim_add(check3, "netdata", NULL, 1, 1, RRDDIM_ABSOLUTE);
-    rrddim_add(check3, "apps.plugin", NULL, 1, 1, RRDDIM_ABSOLUTE);
+    check3 = rrdset_create_localhost("netdata", "checkdt", NULL, "netdata", NULL, "Clock difference"
+                                     , "microseconds diff", 99999, localhost->rrd_update_every, RRDSET_TYPE_LINE);
+    rrddim_add(check3, "caller", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+    rrddim_add(check3, "netdata", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+    rrddim_add(check3, "apps.plugin", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
 
     now_realtime_timeval(&last);
     while(1) {
@@ -39,8 +42,8 @@ void *checks_main(void *ptr) {
         usec = loop_usec - susec;
         debug(D_PROCNETDEV_LOOP, "CHECK: last loop took %llu usec (worked for %llu, sleeped for %llu).", loop_usec, usec, susec);
 
-        if(usec < (rrd_update_every * USEC_PER_SEC / 2ULL)) susec = (rrd_update_every * USEC_PER_SEC) - usec;
-        else susec = rrd_update_every * USEC_PER_SEC / 2ULL;
+        if(usec < (localhost->rrd_update_every * USEC_PER_SEC / 2ULL)) susec = (localhost->rrd_update_every * USEC_PER_SEC) - usec;
+        else susec = localhost->rrd_update_every * USEC_PER_SEC / 2ULL;
 
         // --------------------------------------------------------------------
         // Calculate loop time
@@ -68,7 +71,7 @@ void *checks_main(void *ptr) {
         // --------------------------------------------------------------------
         // check chart 3
 
-        if(!apps_cpu) apps_cpu = rrdset_find("apps.cpu");
+        if(!apps_cpu) apps_cpu = rrdset_find_localhost("apps.cpu");
         if(check3->counter_done) rrdset_next_usec(check3, loop_usec);
         now_realtime_timeval(&loop);
         rrddim_set(check3, "caller", (long long) dt_usec(&loop, &check1->last_collected_time));
index 4fef148ab6a86da1e82e81f62600b51e850623a5..3490e70edef2f587d758ce0bb1adf61ecb6a77db 100644 (file)
@@ -18,7 +18,7 @@ void *freebsd_main(void *ptr) {
     // keep track of the time each module was called
     unsigned long long sutime_freebsd_sysctl = 0ULL;
 
-    usec_t step = rrd_update_every * USEC_PER_SEC;
+    usec_t step = localhost->rrd_update_every * USEC_PER_SEC;
     heartbeat_t hb;
     heartbeat_init(&hb);
     for(;;) {
@@ -30,7 +30,7 @@ void *freebsd_main(void *ptr) {
 
         if(!vdo_freebsd_sysctl) {
             debug(D_PROCNETDEV_LOOP, "FREEBSD: calling do_freebsd_sysctl().");
-            vdo_freebsd_sysctl = do_freebsd_sysctl(rrd_update_every, hb_dt);
+            vdo_freebsd_sysctl = do_freebsd_sysctl(localhost->rrd_update_every, hb_dt);
         }
         if(unlikely(netdata_exit)) break;
 
index 2d08e02ddf5cc6a877aa3e81a2cdbfbd93548826..2ed78160c93bed78b8d8aea3b63fe08b6734c686 100644 (file)
@@ -19,10 +19,11 @@ void *cpuidlejitter_main(void *ptr) {
         sleep_ms = CPU_IDLEJITTER_SLEEP_TIME_MS;
     }
 
-    RRDSET *st = rrdset_find("system.idlejitter");
+    RRDSET *st = rrdset_find_localhost("system.idlejitter");
     if(!st) {
-        st = rrdset_create("system", "idlejitter", NULL, "processes", NULL, "CPU Idle Jitter", "microseconds lost/s", 9999, rrd_update_every, RRDSET_TYPE_LINE);
-        rrddim_add(st, "jitter", NULL, 1, 1, RRDDIM_ABSOLUTE);
+        st = rrdset_create_localhost("system", "idlejitter", NULL, "processes", NULL, "CPU Idle Jitter"
+                                     , "microseconds lost/s", 9999, localhost->rrd_update_every, RRDSET_TYPE_LINE);
+        rrddim_add(st, "jitter", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
     }
 
     struct timeval before, after;
@@ -30,7 +31,9 @@ void *cpuidlejitter_main(void *ptr) {
     for(counter = 0; 1 ;counter++) {
         usec_t usec = 0, susec = 0;
 
-        while(susec < (rrd_update_every * USEC_PER_SEC)) {
+        if(netdata_exit) break;
+
+        while(susec < (localhost->rrd_update_every * USEC_PER_SEC)) {
 
             now_monotonic_timeval(&before);
             sleep_usec(sleep_ms * 1000);
index 54965c5d4ec92813750f0d00265b54e1458268b6..4e84a084d69f3136a068756f6bacf6a98e7e2a9f 100644 (file)
@@ -22,7 +22,7 @@ void *macos_main(void *ptr) {
     unsigned long long sutime_macos_mach_smi = 0ULL;
     unsigned long long sutime_macos_iokit = 0ULL;
 
-    usec_t step = rrd_update_every * USEC_PER_SEC;
+    usec_t step = localhost->rrd_update_every * USEC_PER_SEC;
     heartbeat_t hb;
     heartbeat_init(&hb);
     for(;;) {
@@ -34,19 +34,19 @@ void *macos_main(void *ptr) {
 
         if(!vdo_macos_sysctl) {
             debug(D_PROCNETDEV_LOOP, "MACOS: calling do_macos_sysctl().");
-            vdo_macos_sysctl = do_macos_sysctl(rrd_update_every, hb_dt);
+            vdo_macos_sysctl = do_macos_sysctl(localhost->rrd_update_every, hb_dt);
         }
         if(unlikely(netdata_exit)) break;
 
         if(!vdo_macos_mach_smi) {
             debug(D_PROCNETDEV_LOOP, "MACOS: calling do_macos_mach_smi().");
-            vdo_macos_mach_smi = do_macos_mach_smi(rrd_update_every, hb_dt);
+            vdo_macos_mach_smi = do_macos_mach_smi(localhost->rrd_update_every, hb_dt);
         }
         if(unlikely(netdata_exit)) break;
 
         if(!vdo_macos_iokit) {
             debug(D_PROCNETDEV_LOOP, "MACOS: calling do_macos_iokit().");
-            vdo_macos_iokit = do_macos_iokit(rrd_update_every, hb_dt);
+            vdo_macos_iokit = do_macos_iokit(localhost->rrd_update_every, hb_dt);
         }
         if(unlikely(netdata_exit)) break;
 
index 7aae33c0cf385bbd22a30b5a0c13c81e2db45ab8..a9e6ebc97d5352304cae9b1e738a104ec5c3dacc 100644 (file)
@@ -128,8 +128,8 @@ void *nfacct_main(void *ptr) {
         usec = dt_usec(&now, &last) - susec;
         debug(D_NFACCT_LOOP, "nfacct.plugin: last loop took %llu usec (worked for %llu, sleeped for %llu).", usec + susec, usec, susec);
 
-        if(usec < (rrd_update_every * 1000000ULL / 2ULL)) susec = (rrd_update_every * 1000000ULL) - usec;
-        else susec = rrd_update_every * 1000000ULL / 2ULL;
+        if(usec < (default_rrd_update_every * 1000000ULL / 2ULL)) susec = (default_rrd_update_every * 1000000ULL) - usec;
+        else susec = default_rrd_update_every * 1000000ULL / 2ULL;
 
 
         // --------------------------------------------------------------------
@@ -139,17 +139,17 @@ void *nfacct_main(void *ptr) {
 
             st = rrdset_find_bytype("netfilter", "nfacct_packets");
             if(!st) {
-                st = rrdset_create("netfilter", "nfacct_packets", NULL, "nfacct", NULL, "Netfilter Accounting Packets", "packets/s", 3206, rrd_update_every, RRDSET_TYPE_STACKED);
+                st = rrdset_create("netfilter", "nfacct_packets", NULL, "nfacct", NULL, "Netfilter Accounting Packets", "packets/s", 3206, default_rrd_update_every, RRDSET_TYPE_STACKED);
 
                 for(i = 0; i < nfacct_list->len ; i++)
-                    rrddim_add(st, nfacct_list->data[i].name, NULL, 1, rrd_update_every, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, nfacct_list->data[i].name, NULL, 1, default_rrd_update_every, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(st);
 
             for(i = 0; i < nfacct_list->len ; i++) {
                 RRDDIM *rd = rrddim_find(st, nfacct_list->data[i].name);
 
-                if(!rd) rd = rrddim_add(st, nfacct_list->data[i].name, NULL, 1, rrd_update_every, RRDDIM_INCREMENTAL);
+                if(!rd) rd = rrddim_add(st, nfacct_list->data[i].name, NULL, 1, default_rrd_update_every, RRD_ALGORITHM_INCREMENTAL);
                 if(rd) rrddim_set_by_pointer(st, rd, nfacct_list->data[i].pkts);
             }
 
@@ -159,17 +159,17 @@ void *nfacct_main(void *ptr) {
 
             st = rrdset_find_bytype("netfilter", "nfacct_bytes");
             if(!st) {
-                st = rrdset_create("netfilter", "nfacct_bytes", NULL, "nfacct", NULL, "Netfilter Accounting Bandwidth", "kilobytes/s", 3207, rrd_update_every, RRDSET_TYPE_STACKED);
+                st = rrdset_create("netfilter", "nfacct_bytes", NULL, "nfacct", NULL, "Netfilter Accounting Bandwidth", "kilobytes/s", 3207, default_rrd_update_every, RRDSET_TYPE_STACKED);
 
                 for(i = 0; i < nfacct_list->len ; i++)
-                    rrddim_add(st, nfacct_list->data[i].name, NULL, 1, 1000 * rrd_update_every, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, nfacct_list->data[i].name, NULL, 1, 1000 * default_rrd_update_every, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(st);
 
             for(i = 0; i < nfacct_list->len ; i++) {
                 RRDDIM *rd = rrddim_find(st, nfacct_list->data[i].name);
 
-                if(!rd) rd = rrddim_add(st, nfacct_list->data[i].name, NULL, 1, 1000 * rrd_update_every, RRDDIM_INCREMENTAL);
+                if(!rd) rd = rrddim_add(st, nfacct_list->data[i].name, NULL, 1, 1000 * default_rrd_update_every, RRD_ALGORITHM_INCREMENTAL);
                 if(rd) rrddim_set_by_pointer(st, rd, nfacct_list->data[i].bytes);
             }
 
index 215925a58e0097e07639735bfffaabfc38d71c57..2ca77491de4633736f6ea43e1b17f091b3bdf3d9 100644 (file)
@@ -79,7 +79,7 @@ void *proc_main(void *ptr) {
         pm->rd = NULL;
     }
 
-    usec_t step = rrd_update_every * USEC_PER_SEC;
+    usec_t step = localhost->rrd_update_every * USEC_PER_SEC;
     heartbeat_t hb;
     heartbeat_init(&hb);
 
@@ -97,7 +97,7 @@ void *proc_main(void *ptr) {
 
             debug(D_PROCNETDEV_LOOP, "PROC calling %s.", pm->name);
 
-            pm->enabled = !pm->func(rrd_update_every, hb_dt);
+            pm->enabled = !pm->func(localhost->rrd_update_every, hb_dt);
             pm->duration = heartbeat_dt_usec(&hb) - duration;
             duration += pm->duration;
 
@@ -112,16 +112,18 @@ void *proc_main(void *ptr) {
             static RRDSET *st = NULL;
 
             if(unlikely(!st)) {
-                st = rrdset_find_bytype("netdata", "plugin_proc_modules");
+                st = rrdset_find_bytype_localhost("netdata", "plugin_proc_modules");
 
                 if(!st) {
-                    st = rrdset_create("netdata", "plugin_proc_modules", NULL, "proc", NULL, "NetData Proc Plugin Modules Durations", "milliseconds/run", 132001, rrd_update_every, RRDSET_TYPE_STACKED);
+                    st = rrdset_create_localhost("netdata", "plugin_proc_modules", NULL, "proc", NULL
+                                                 , "NetData Proc Plugin Modules Durations", "milliseconds/run", 132001
+                                                 , localhost->rrd_update_every, RRDSET_TYPE_STACKED);
 
                     for(i = 0 ; proc_modules[i].name ;i++) {
                         struct proc_module *pm = &proc_modules[i];
                         if(unlikely(!pm->enabled)) continue;
 
-                        pm->rd = rrddim_add(st, pm->dim, NULL, 1, 1000, RRDDIM_ABSOLUTE);
+                        pm->rd = rrddim_add(st, pm->dim, NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
                     }
                 }
             }
index 689efe7c7a0d93a3fbbe4b9cf9494bff0d848dd8..6657ffe783e1d1ade18139b375b3fbc6ce5b2eb8 100644 (file)
@@ -1,6 +1,8 @@
 #include "common.h"
 
 #define DELAULT_EXLUDED_PATHS "/proc/* /sys/* /var/run/user/* /run/user/*"
+#define DEFAULT_EXCLUDED_FILESYSTEMS ""
+#define CONFIG_SECTION_DISKSPACE "plugin:proc:diskspace"
 
 static struct mountinfo *disk_mountinfo_root = NULL;
 static int check_for_new_mountpoints_every = 15;
@@ -14,7 +16,7 @@ static inline void mountinfo_reload(int force) {
         mountinfo_free(disk_mountinfo_root);
 
         // re-read mountinfo in case something changed
-        disk_mountinfo_root = mountinfo_read(1);
+        disk_mountinfo_root = mountinfo_read(0);
 
         last_loaded = now;
     }
@@ -25,6 +27,7 @@ static inline void mountinfo_reload(int force) {
 struct mount_point_metadata {
     int do_space;
     int do_inodes;
+    int shown_error;
 
     size_t collected; // the number of times this has been collected
 
@@ -45,22 +48,28 @@ static inline void do_disk_space_stats(struct mountinfo *mi, int update_every) {
 
     static DICTIONARY *mount_points = NULL;
     static SIMPLE_PATTERN *excluded_mountpoints = NULL;
+    static SIMPLE_PATTERN *excluded_filesystems = NULL;
     int do_space, do_inodes;
 
     if(unlikely(!mount_points)) {
-        const char *s;
         SIMPLE_PREFIX_MODE mode = SIMPLE_PATTERN_EXACT;
 
-        if(config_exists("plugin:proc:/proc/diskstats", "exclude space metrics on paths") && !config_exists("plugin:proc:diskspace", "exclude space metrics on paths")) {
-            // the config exists in the old section
-            s = config_get("plugin:proc:/proc/diskstats", "exclude space metrics on paths", DELAULT_EXLUDED_PATHS);
+        if(config_move("plugin:proc:/proc/diskstats", "exclude space metrics on paths", CONFIG_SECTION_DISKSPACE, "exclude space metrics on paths") != -1) {
+            // old configuration, enable backwards compatibility
             mode = SIMPLE_PATTERN_PREFIX;
         }
-        else
-            s = config_get("plugin:proc:diskspace", "exclude space metrics on paths", DELAULT_EXLUDED_PATHS);
+
+        excluded_mountpoints = simple_pattern_create(
+                config_get(CONFIG_SECTION_DISKSPACE, "exclude space metrics on paths", DELAULT_EXLUDED_PATHS),
+                mode
+        );
+
+        excluded_filesystems = simple_pattern_create(
+                config_get(CONFIG_SECTION_DISKSPACE, "exclude space metrics on filesystems", DEFAULT_EXCLUDED_FILESYSTEMS),
+                SIMPLE_PATTERN_EXACT
+        );
 
         mount_points = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED);
-        excluded_mountpoints = simple_pattern_create(s, mode);
     }
 
     struct mount_point_metadata *m = dictionary_get(mount_points, mi->mount_point);
@@ -68,12 +77,17 @@ static inline void do_disk_space_stats(struct mountinfo *mi, int update_every) {
         char var_name[4096 + 1];
         snprintfz(var_name, 4096, "plugin:proc:diskspace:%s", mi->mount_point);
 
-        int def_space = config_get_boolean_ondemand("plugin:proc:diskspace", "space usage for all disks", CONFIG_ONDEMAND_ONDEMAND);
-        int def_inodes = config_get_boolean_ondemand("plugin:proc:diskspace", "inodes usage for all disks", CONFIG_ONDEMAND_ONDEMAND);
+        int def_space = config_get_boolean_ondemand(CONFIG_SECTION_DISKSPACE, "space usage for all disks", CONFIG_BOOLEAN_AUTO);
+        int def_inodes = config_get_boolean_ondemand(CONFIG_SECTION_DISKSPACE, "inodes usage for all disks", CONFIG_BOOLEAN_AUTO);
 
         if(unlikely(simple_pattern_matches(excluded_mountpoints, mi->mount_point))) {
-            def_space = CONFIG_ONDEMAND_NO;
-            def_inodes = CONFIG_ONDEMAND_NO;
+            def_space = CONFIG_BOOLEAN_NO;
+            def_inodes = CONFIG_BOOLEAN_NO;
+        }
+
+        if(unlikely(simple_pattern_matches(excluded_filesystems, mi->filesystem))) {
+            def_space = CONFIG_BOOLEAN_NO;
+            def_inodes = CONFIG_BOOLEAN_NO;
         }
 
         do_space = config_get_boolean_ondemand(var_name, "space usage", def_space);
@@ -82,6 +96,7 @@ static inline void do_disk_space_stats(struct mountinfo *mi, int update_every) {
         struct mount_point_metadata mp = {
                 .do_space = do_space,
                 .do_inodes = do_inodes,
+                .shown_error = 0,
 
                 .collected = 0,
 
@@ -98,12 +113,8 @@ static inline void do_disk_space_stats(struct mountinfo *mi, int update_every) {
 
         m = dictionary_set(mount_points, mi->mount_point, &mp, sizeof(struct mount_point_metadata));
     }
-    else {
-        do_space = m->do_space;
-        do_inodes = m->do_inodes;
-    }
 
-    if(unlikely(do_space == CONFIG_ONDEMAND_NO && do_inodes == CONFIG_ONDEMAND_NO))
+    if(unlikely(m->do_space == CONFIG_BOOLEAN_NO && m->do_inodes == CONFIG_BOOLEAN_NO))
         return;
 
     if(unlikely(mi->flags & MOUNTINFO_READONLY && !m->collected))
@@ -111,9 +122,18 @@ static inline void do_disk_space_stats(struct mountinfo *mi, int update_every) {
 
     struct statvfs buff_statvfs;
     if (statvfs(mi->mount_point, &buff_statvfs) < 0) {
-        error("Failed statvfs() for '%s' (disk '%s')", mi->mount_point, disk);
+        if(!m->shown_error) {
+            error("Failed statvfs() for '%s' (disk '%s', filesystem '%s', root '%s')"
+                  , mi->mount_point
+                  , disk
+                  , mi->filesystem?mi->filesystem:""
+                  , mi->root?mi->root:""
+            );
+            m->shown_error = 1;
+        }
         return;
     }
+    m->shown_error = 0;
 
     // logic found at get_fs_usage() in coreutils
     unsigned long bsize = (buff_statvfs.f_frsize) ? buff_statvfs.f_frsize : buff_statvfs.f_bsize;
@@ -150,19 +170,30 @@ static inline void do_disk_space_stats(struct mountinfo *mi, int update_every) {
 
     int rendered = 0;
 
-    if(do_space == CONFIG_ONDEMAND_YES || (do_space == CONFIG_ONDEMAND_ONDEMAND && (bavail || breserved_root || bused))) {
+    if(m->do_space == CONFIG_BOOLEAN_YES || (m->do_space == CONFIG_BOOLEAN_AUTO && (bavail || breserved_root || bused))) {
         if(unlikely(!m->st_space)) {
-            m->do_space = CONFIG_ONDEMAND_YES;
-            m->st_space = rrdset_find_bytype("disk_space", disk);
+            m->do_space = CONFIG_BOOLEAN_YES;
+            m->st_space = rrdset_find_bytype_localhost("disk_space", disk);
             if(unlikely(!m->st_space)) {
                 char title[4096 + 1];
                 snprintfz(title, 4096, "Disk Space Usage for %s [%s]", family, mi->mount_source);
-                m->st_space = rrdset_create("disk_space", disk, NULL, family, "disk.space", title, "GB", 2023, update_every, RRDSET_TYPE_STACKED);
+                m->st_space = rrdset_create_localhost(
+                        "disk_space"
+                        , disk
+                        , NULL
+                        , family
+                        , "disk.space"
+                        , title
+                        , "GB"
+                        , 2023
+                        , update_every
+                        , RRDSET_TYPE_STACKED
+                );
             }
 
-            m->rd_space_avail    = rrddim_add(m->st_space, "avail", NULL, bsize, 1024 * 1024 * 1024, RRDDIM_ABSOLUTE);
-            m->rd_space_used     = rrddim_add(m->st_space, "used", NULL, bsize, 1024 * 1024 * 1024, RRDDIM_ABSOLUTE);
-            m->rd_space_reserved = rrddim_add(m->st_space, "reserved_for_root", "reserved for root", bsize, 1024 * 1024 * 1024, RRDDIM_ABSOLUTE);
+            m->rd_space_avail    = rrddim_add(m->st_space, "avail", NULL, (collected_number)bsize, 1024 * 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
+            m->rd_space_used     = rrddim_add(m->st_space, "used", NULL, (collected_number)bsize, 1024 * 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
+            m->rd_space_reserved = rrddim_add(m->st_space, "reserved_for_root", "reserved for root", (collected_number)bsize, 1024 * 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
         }
         else
             rrdset_next(m->st_space);
@@ -177,19 +208,30 @@ static inline void do_disk_space_stats(struct mountinfo *mi, int update_every) {
 
     // --------------------------------------------------------------------------
 
-    if(do_inodes == CONFIG_ONDEMAND_YES || (do_inodes == CONFIG_ONDEMAND_ONDEMAND && (favail || freserved_root || fused))) {
+    if(m->do_inodes == CONFIG_BOOLEAN_YES || (m->do_inodes == CONFIG_BOOLEAN_AUTO && (favail || freserved_root || fused))) {
         if(unlikely(!m->st_inodes)) {
-            m->do_inodes = CONFIG_ONDEMAND_YES;
-            m->st_inodes = rrdset_find_bytype("disk_inodes", disk);
+            m->do_inodes = CONFIG_BOOLEAN_YES;
+            m->st_inodes = rrdset_find_bytype_localhost("disk_inodes", disk);
             if(unlikely(!m->st_inodes)) {
                 char title[4096 + 1];
                 snprintfz(title, 4096, "Disk Files (inodes) Usage for %s [%s]", family, mi->mount_source);
-                m->st_inodes = rrdset_create("disk_inodes", disk, NULL, family, "disk.inodes", title, "Inodes", 2024, update_every, RRDSET_TYPE_STACKED);
+                m->st_inodes = rrdset_create_localhost(
+                        "disk_inodes"
+                        , disk
+                        , NULL
+                        , family
+                        , "disk.inodes"
+                        , title
+                        , "Inodes"
+                        , 2024
+                        , update_every
+                        , RRDSET_TYPE_STACKED
+                );
             }
 
-            m->rd_inodes_avail    = rrddim_add(m->st_inodes, "avail", NULL, 1, 1, RRDDIM_ABSOLUTE);
-            m->rd_inodes_used     = rrddim_add(m->st_inodes, "used", NULL, 1, 1, RRDDIM_ABSOLUTE);
-            m->rd_inodes_reserved = rrddim_add(m->st_inodes, "reserved_for_root", "reserved for root", 1, 1, RRDDIM_ABSOLUTE);
+            m->rd_inodes_avail    = rrddim_add(m->st_inodes, "avail", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+            m->rd_inodes_used     = rrddim_add(m->st_inodes, "used", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+            m->rd_inodes_reserved = rrddim_add(m->st_inodes, "reserved_for_root", "reserved for root", 1, 1, RRD_ALGORITHM_ABSOLUTE);
         }
         else
             rrdset_next(m->st_inodes);
@@ -221,11 +263,11 @@ void *proc_diskspace_main(void *ptr) {
 
     int vdo_cpu_netdata = config_get_boolean("plugin:proc", "netdata server resources", 1);
 
-    int update_every = (int)config_get_number("plugin:proc:diskspace", "update every", rrd_update_every);
-    if(update_every < rrd_update_every)
-        update_every = rrd_update_every;
+    int update_every = (int)config_get_number(CONFIG_SECTION_DISKSPACE, "update every", localhost->rrd_update_every);
+    if(update_every < localhost->rrd_update_every)
+        update_every = localhost->rrd_update_every;
 
-    check_for_new_mountpoints_every = (int)config_get_number("plugin:proc:diskspace", "check for new mount points every", check_for_new_mountpoints_every);
+    check_for_new_mountpoints_every = (int)config_get_number(CONFIG_SECTION_DISKSPACE, "check for new mount points every", check_for_new_mountpoints_every);
     if(check_for_new_mountpoints_every < update_every)
         check_for_new_mountpoints_every = update_every;
 
@@ -272,13 +314,23 @@ void *proc_diskspace_main(void *ptr) {
             getrusage(RUSAGE_THREAD, &thread);
 
             if(!stcpu_thread) {
-                stcpu_thread = rrdset_find("netdata.plugin_diskspace");
-                if(!stcpu_thread) stcpu_thread = rrdset_create("netdata", "plugin_diskspace", NULL, "diskspace", NULL
-                                                 , "NetData Disk Space Plugin CPU usage", "milliseconds/s", 132020
-                                                 , update_every, RRDSET_TYPE_STACKED);
-
-                rd_user   = rrddim_add(stcpu_thread, "user", NULL, 1, 1000, RRDDIM_INCREMENTAL);
-                rd_system = rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);
+                stcpu_thread = rrdset_find_localhost("netdata.plugin_diskspace");
+                if(!stcpu_thread)
+                    stcpu_thread = rrdset_create_localhost(
+                            "netdata"
+                            , "plugin_diskspace"
+                            , NULL
+                            , "diskspace"
+                            , NULL
+                            , "NetData Disk Space Plugin CPU usage"
+                            , "milliseconds/s"
+                            , 132020
+                            , update_every
+                            , RRDSET_TYPE_STACKED
+                    );
+
+                rd_user   = rrddim_add(stcpu_thread, "user", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL);
+                rd_system = rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL);
             }
             else
                 rrdset_next(stcpu_thread);
@@ -290,12 +342,22 @@ void *proc_diskspace_main(void *ptr) {
             // ----------------------------------------------------------------
 
             if(!st_duration) {
-                st_duration = rrdset_find("netdata.plugin_diskspace_dt");
-                if(!st_duration) st_duration = rrdset_create("netdata", "plugin_diskspace_dt", NULL, "diskspace", NULL
-                                                 , "NetData Disk Space Plugin Duration", "milliseconds/run", 132021
-                                                 , update_every, RRDSET_TYPE_AREA);
-
-                rd_duration = rrddim_add(st_duration, "duration", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+                st_duration = rrdset_find_localhost("netdata.plugin_diskspace_dt");
+                if(!st_duration)
+                    st_duration = rrdset_create_localhost(
+                            "netdata"
+                            , "plugin_diskspace_dt"
+                            , NULL
+                            , "diskspace"
+                            , NULL
+                            , "NetData Disk Space Plugin Duration"
+                            , "milliseconds/run"
+                            , 132021
+                            , update_every
+                            , RRDSET_TYPE_AREA
+                    );
+
+                rd_duration = rrddim_add(st_duration, "duration", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
             }
             else
                 rrdset_next(st_duration);
index 3cb64505469d4498b161a1c4d80cf48dd7466c82..35763d316aceb6e05ed556266011d16abb372c17 100644 (file)
@@ -190,13 +190,13 @@ static inline void tc_device_commit(struct tc_device *d) {
     static int enable_new_interfaces = -1, enable_bytes = -1, enable_packets = -1, enable_dropped = -1, enable_tokens = -1, enable_ctokens = -1, enabled_all_classes_qdiscs = -1;
 
     if(unlikely(enable_new_interfaces == -1)) {
-        enable_new_interfaces      = config_get_boolean_ondemand("plugin:tc", "enable new interfaces detected at runtime", CONFIG_ONDEMAND_YES);
-        enable_bytes               = config_get_boolean_ondemand("plugin:tc", "enable traffic charts for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
-        enable_packets             = config_get_boolean_ondemand("plugin:tc", "enable packets charts for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
-        enable_dropped             = config_get_boolean_ondemand("plugin:tc", "enable dropped charts for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
-        enable_tokens              = config_get_boolean_ondemand("plugin:tc", "enable tokens charts for all interfaces", CONFIG_ONDEMAND_NO);
-        enable_ctokens             = config_get_boolean_ondemand("plugin:tc", "enable ctokens charts for all interfaces", CONFIG_ONDEMAND_NO);
-        enabled_all_classes_qdiscs = config_get_boolean_ondemand("plugin:tc", "enable show all classes and qdiscs for all interfaces", CONFIG_ONDEMAND_NO);
+        enable_new_interfaces      = config_get_boolean_ondemand("plugin:tc", "enable new interfaces detected at runtime", CONFIG_BOOLEAN_YES);
+        enable_bytes               = config_get_boolean_ondemand("plugin:tc", "enable traffic charts for all interfaces", CONFIG_BOOLEAN_AUTO);
+        enable_packets             = config_get_boolean_ondemand("plugin:tc", "enable packets charts for all interfaces", CONFIG_BOOLEAN_AUTO);
+        enable_dropped             = config_get_boolean_ondemand("plugin:tc", "enable dropped charts for all interfaces", CONFIG_BOOLEAN_AUTO);
+        enable_tokens              = config_get_boolean_ondemand("plugin:tc", "enable tokens charts for all interfaces", CONFIG_BOOLEAN_NO);
+        enable_ctokens             = config_get_boolean_ondemand("plugin:tc", "enable ctokens charts for all interfaces", CONFIG_BOOLEAN_NO);
+        enabled_all_classes_qdiscs = config_get_boolean_ondemand("plugin:tc", "enable show all classes and qdiscs for all interfaces", CONFIG_BOOLEAN_NO);
     }
 
     if(unlikely(d->enabled == (char)-1)) {
@@ -370,11 +370,15 @@ static inline void tc_device_commit(struct tc_device *d) {
     // --------------------------------------------------------------------
     // bytes
 
-    if(d->enabled_bytes == CONFIG_ONDEMAND_YES || (d->enabled_bytes == CONFIG_ONDEMAND_ONDEMAND && bytes_sum)) {
-        d->enabled_bytes = CONFIG_ONDEMAND_YES;
+    if(d->enabled_bytes == CONFIG_BOOLEAN_YES || (d->enabled_bytes == CONFIG_BOOLEAN_AUTO && bytes_sum)) {
+        d->enabled_bytes = CONFIG_BOOLEAN_YES;
 
         if(unlikely(!d->st_bytes))
-            d->st_bytes = rrdset_create(RRD_TYPE_TC, d->id, d->name?d->name:d->id, d->family?d->family:d->id, RRD_TYPE_TC ".qos", "Class Usage", "kilobits/s", 7000, rrd_update_every, d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE : RRDSET_TYPE_STACKED);
+            d->st_bytes = rrdset_create_localhost(RRD_TYPE_TC, d->id, d->name ? d->name : d->id
+                                                  , d->family ? d->family : d->id, RRD_TYPE_TC ".qos", "Class Usage"
+                                                  , "kilobits/s", 7000, localhost->rrd_update_every
+                                                  , d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE
+                                                                                  : RRDSET_TYPE_STACKED);
 
         else {
             rrdset_next(d->st_bytes);
@@ -388,7 +392,7 @@ static inline void tc_device_commit(struct tc_device *d) {
             if(unlikely(!c->render)) continue;
 
             if(unlikely(!c->rd_bytes))
-                c->rd_bytes = rrddim_add(d->st_bytes, c->id, c->name?c->name:c->id, 8, 1024, RRDDIM_INCREMENTAL);
+                c->rd_bytes = rrddim_add(d->st_bytes, c->id, c->name?c->name:c->id, 8, 1024, RRD_ALGORITHM_INCREMENTAL);
             else if(unlikely(c->name_updated))
                 rrddim_set_name(d->st_bytes, c->rd_bytes, c->name);
 
@@ -400,8 +404,8 @@ static inline void tc_device_commit(struct tc_device *d) {
     // --------------------------------------------------------------------
     // packets
 
-    if(d->enabled_packets == CONFIG_ONDEMAND_YES || (d->enabled_packets == CONFIG_ONDEMAND_ONDEMAND && packets_sum)) {
-        d->enabled_packets = CONFIG_ONDEMAND_YES;
+    if(d->enabled_packets == CONFIG_BOOLEAN_YES || (d->enabled_packets == CONFIG_BOOLEAN_AUTO && packets_sum)) {
+        d->enabled_packets = CONFIG_BOOLEAN_YES;
 
         if(unlikely(!d->st_packets)) {
             char id[RRD_ID_LENGTH_MAX + 1];
@@ -409,7 +413,10 @@ static inline void tc_device_commit(struct tc_device *d) {
             snprintfz(id, RRD_ID_LENGTH_MAX, "%s_packets", d->id);
             snprintfz(name, RRD_ID_LENGTH_MAX, "%s_packets", d->name?d->name:d->id);
 
-            d->st_packets = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_packets", "Class Packets", "packets/s", 7010, rrd_update_every, d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE : RRDSET_TYPE_STACKED);
+            d->st_packets = rrdset_create_localhost(RRD_TYPE_TC, id, name, d->family ? d->family : d->id
+                                                    , RRD_TYPE_TC ".qos_packets", "Class Packets", "packets/s", 7010
+                                                    , localhost->rrd_update_every, d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE
+                                                                                                      : RRDSET_TYPE_STACKED);
         }
         else {
             rrdset_next(d->st_packets);
@@ -428,7 +435,7 @@ static inline void tc_device_commit(struct tc_device *d) {
             if(unlikely(!c->render)) continue;
 
             if(unlikely(!c->rd_packets))
-                c->rd_packets = rrddim_add(d->st_packets, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_INCREMENTAL);
+                c->rd_packets = rrddim_add(d->st_packets, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_INCREMENTAL);
             else if(unlikely(c->name_updated))
                 rrddim_set_name(d->st_packets, c->rd_packets, c->name);
 
@@ -440,8 +447,8 @@ static inline void tc_device_commit(struct tc_device *d) {
     // --------------------------------------------------------------------
     // dropped
 
-    if(d->enabled_dropped == CONFIG_ONDEMAND_YES || (d->enabled_dropped == CONFIG_ONDEMAND_ONDEMAND && dropped_sum)) {
-        d->enabled_dropped = CONFIG_ONDEMAND_YES;
+    if(d->enabled_dropped == CONFIG_BOOLEAN_YES || (d->enabled_dropped == CONFIG_BOOLEAN_AUTO && dropped_sum)) {
+        d->enabled_dropped = CONFIG_BOOLEAN_YES;
 
         if(unlikely(!d->st_dropped)) {
             char id[RRD_ID_LENGTH_MAX + 1];
@@ -449,7 +456,11 @@ static inline void tc_device_commit(struct tc_device *d) {
             snprintfz(id, RRD_ID_LENGTH_MAX, "%s_dropped", d->id);
             snprintfz(name, RRD_ID_LENGTH_MAX, "%s_dropped", d->name?d->name:d->id);
 
-            d->st_dropped = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_dropped", "Class Dropped Packets", "packets/s", 7020, rrd_update_every, d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE : RRDSET_TYPE_STACKED);
+            d->st_dropped = rrdset_create_localhost(RRD_TYPE_TC, id, name, d->family ? d->family : d->id
+                                                    , RRD_TYPE_TC ".qos_dropped", "Class Dropped Packets", "packets/s"
+                                                    , 7020, localhost->rrd_update_every
+                                                    , d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE
+                                                                                    : RRDSET_TYPE_STACKED);
         }
         else {
             rrdset_next(d->st_dropped);
@@ -468,7 +479,7 @@ static inline void tc_device_commit(struct tc_device *d) {
             if(unlikely(!c->render)) continue;
 
             if(unlikely(!c->rd_dropped))
-                c->rd_dropped = rrddim_add(d->st_dropped, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_INCREMENTAL);
+                c->rd_dropped = rrddim_add(d->st_dropped, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_INCREMENTAL);
             else if(unlikely(c->name_updated))
                 rrddim_set_name(d->st_dropped, c->rd_dropped, c->name);
 
@@ -480,8 +491,8 @@ static inline void tc_device_commit(struct tc_device *d) {
     // --------------------------------------------------------------------
     // tokens
 
-    if(d->enabled_tokens == CONFIG_ONDEMAND_YES || (d->enabled_tokens == CONFIG_ONDEMAND_ONDEMAND && tokens_sum)) {
-        d->enabled_tokens = CONFIG_ONDEMAND_YES;
+    if(d->enabled_tokens == CONFIG_BOOLEAN_YES || (d->enabled_tokens == CONFIG_BOOLEAN_AUTO && tokens_sum)) {
+        d->enabled_tokens = CONFIG_BOOLEAN_YES;
 
         if(unlikely(!d->st_tokens)) {
             char id[RRD_ID_LENGTH_MAX + 1];
@@ -489,7 +500,9 @@ static inline void tc_device_commit(struct tc_device *d) {
             snprintfz(id, RRD_ID_LENGTH_MAX, "%s_tokens", d->id);
             snprintfz(name, RRD_ID_LENGTH_MAX, "%s_tokens", d->name?d->name:d->id);
 
-            d->st_tokens = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_tokens", "Class Tokens", "tokens", 7030, rrd_update_every, RRDSET_TYPE_LINE);
+            d->st_tokens = rrdset_create_localhost(RRD_TYPE_TC, id, name, d->family ? d->family : d->id
+                                                   , RRD_TYPE_TC ".qos_tokens", "Class Tokens", "tokens", 7030
+                                                   , localhost->rrd_update_every, RRDSET_TYPE_LINE);
         }
         else {
             rrdset_next(d->st_tokens);
@@ -508,7 +521,7 @@ static inline void tc_device_commit(struct tc_device *d) {
             if(unlikely(!c->render)) continue;
 
             if(unlikely(!c->rd_tokens)) {
-                c->rd_tokens = rrddim_add(d->st_tokens, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_ABSOLUTE);
+                c->rd_tokens = rrddim_add(d->st_tokens, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_ABSOLUTE);
             }
             else if(unlikely(c->name_updated))
                 rrddim_set_name(d->st_tokens, c->rd_tokens, c->name);
@@ -521,8 +534,8 @@ static inline void tc_device_commit(struct tc_device *d) {
     // --------------------------------------------------------------------
     // ctokens
 
-    if(d->enabled_ctokens == CONFIG_ONDEMAND_YES || (d->enabled_ctokens == CONFIG_ONDEMAND_ONDEMAND && ctokens_sum)) {
-        d->enabled_ctokens = CONFIG_ONDEMAND_YES;
+    if(d->enabled_ctokens == CONFIG_BOOLEAN_YES || (d->enabled_ctokens == CONFIG_BOOLEAN_AUTO && ctokens_sum)) {
+        d->enabled_ctokens = CONFIG_BOOLEAN_YES;
 
         if(unlikely(!d->st_ctokens)) {
             char id[RRD_ID_LENGTH_MAX + 1];
@@ -530,7 +543,9 @@ static inline void tc_device_commit(struct tc_device *d) {
             snprintfz(id, RRD_ID_LENGTH_MAX, "%s_ctokens", d->id);
             snprintfz(name, RRD_ID_LENGTH_MAX, "%s_ctokens", d->name?d->name:d->id);
 
-            d->st_ctokens = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_ctokens", "Class cTokens", "ctokens", 7040, rrd_update_every, RRDSET_TYPE_LINE);
+            d->st_ctokens = rrdset_create_localhost(RRD_TYPE_TC, id, name, d->family ? d->family : d->id
+                                                    , RRD_TYPE_TC ".qos_ctokens", "Class cTokens", "ctokens", 7040
+                                                    , localhost->rrd_update_every, RRDSET_TYPE_LINE);
         }
         else {
             debug(D_TC_LOOP, "TC: Updating _ctokens chart for device '%s'", d->name?d->name:d->id);
@@ -550,7 +565,7 @@ static inline void tc_device_commit(struct tc_device *d) {
             if(unlikely(!c->render)) continue;
 
             if(unlikely(!c->rd_ctokens))
-                c->rd_ctokens = rrddim_add(d->st_ctokens, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_ABSOLUTE);
+                c->rd_ctokens = rrddim_add(d->st_ctokens, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_ABSOLUTE);
             else if(unlikely(c->name_updated))
                 rrddim_set_name(d->st_ctokens, c->rd_ctokens, c->name);
 
@@ -792,7 +807,7 @@ void *tc_main(void *ptr) {
         struct tc_device *device = NULL;
         struct tc_class *class = NULL;
 
-        snprintfz(buffer, TC_LINE_MAX, "exec %s %d", tc_script, rrd_update_every);
+        snprintfz(buffer, TC_LINE_MAX, "exec %s %d", tc_script, localhost->rrd_update_every);
         debug(D_TC_LOOP, "executing '%s'", buffer);
 
         fp = mypopen(buffer, (pid_t *)&tc_child_pid);
@@ -974,11 +989,13 @@ void *tc_main(void *ptr) {
                 // debug(D_TC_LOOP, "WORKTIME line '%s' '%s'", words[1], words[2]);
                 getrusage(RUSAGE_THREAD, &thread);
 
-                if(unlikely(!stcpu)) stcpu = rrdset_find("netdata.plugin_tc_cpu");
+                if(unlikely(!stcpu)) stcpu = rrdset_find_localhost("netdata.plugin_tc_cpu");
                 if(unlikely(!stcpu)) {
-                    stcpu = rrdset_create("netdata", "plugin_tc_cpu", NULL, "tc.helper", NULL, "NetData TC CPU usage", "milliseconds/s", 135000, rrd_update_every, RRDSET_TYPE_STACKED);
-                    rrddim_add(stcpu, "user",  NULL,  1, 1000, RRDDIM_INCREMENTAL);
-                    rrddim_add(stcpu, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);
+                    stcpu = rrdset_create_localhost("netdata", "plugin_tc_cpu", NULL, "tc.helper", NULL
+                                                    , "NetData TC CPU usage", "milliseconds/s", 135000, localhost->rrd_update_every
+                                                    , RRDSET_TYPE_STACKED);
+                    rrddim_add(stcpu, "user",  NULL,  1, 1000, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(stcpu, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(stcpu);
 
@@ -986,10 +1003,12 @@ void *tc_main(void *ptr) {
                 rrddim_set(stcpu, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec);
                 rrdset_done(stcpu);
 
-                if(unlikely(!sttime)) stcpu = rrdset_find("netdata.plugin_tc_time");
+                if(unlikely(!sttime)) stcpu = rrdset_find_localhost("netdata.plugin_tc_time");
                 if(unlikely(!sttime)) {
-                    sttime = rrdset_create("netdata", "plugin_tc_time", NULL, "tc.helper", NULL, "NetData TC script execution", "milliseconds/run", 135001, rrd_update_every, RRDSET_TYPE_AREA);
-                    rrddim_add(sttime, "run_time",  "run time",  1, 1, RRDDIM_ABSOLUTE);
+                    sttime = rrdset_create_localhost("netdata", "plugin_tc_time", NULL, "tc.helper", NULL
+                                                     , "NetData TC script execution", "milliseconds/run", 135001
+                                                     , localhost->rrd_update_every, RRDSET_TYPE_AREA);
+                    rrddim_add(sttime, "run_time",  "run time",  1, 1, RRD_ALGORITHM_ABSOLUTE);
                 }
                 else rrdset_next(sttime);
 
@@ -1037,7 +1056,7 @@ void *tc_main(void *ptr) {
             goto cleanup;
         }
 
-        sleep((unsigned int) rrd_update_every);
+        sleep((unsigned int) localhost->rrd_update_every);
     }
 
 cleanup:
index 9a027cb2f7360c56d486951acfbc6cf4dcb1099d..7fa19eaf05467266beaeab5df8f933a5dc6a2542 100644 (file)
@@ -83,302 +83,315 @@ static int pluginsd_split_words(char *str, char **words, int max_words) {
     return i;
 }
 
+inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int trust_durations) {
+    int enabled = cd->enabled;
 
-void *pluginsd_worker_thread(void *arg)
-{
-    struct plugind *cd = (struct plugind *)arg;
-    cd->obsolete = 0;
+    if(!fp || !enabled) {
+        cd->enabled = 0;
+        return 0;
+    }
 
-    char line[PLUGINSD_LINE_MAX + 1];
+    size_t count = 0;
 
-#ifdef DETACH_PLUGINS_FROM_NETDATA
-    usec_t usec = 0, susec = 0;
-    struct timeval last = {0, 0} , now = {0, 0};
-#endif
+    char line[PLUGINSD_LINE_MAX + 1];
 
     char *words[MAX_WORDS] = { NULL };
+    /* uint32_t HOST_HASH = simple_hash("HOST"); */
     uint32_t BEGIN_HASH = simple_hash("BEGIN");
     uint32_t END_HASH = simple_hash("END");
     uint32_t FLUSH_HASH = simple_hash("FLUSH");
     uint32_t CHART_HASH = simple_hash("CHART");
     uint32_t DIMENSION_HASH = simple_hash("DIMENSION");
     uint32_t DISABLE_HASH = simple_hash("DISABLE");
-#ifdef DETACH_PLUGINS_FROM_NETDATA
-    uint32_t MYPID_HASH = simple_hash("MYPID");
-    uint32_t STOPPING_WAKE_ME_UP_PLEASE_HASH = simple_hash("STOPPING_WAKE_ME_UP_PLEASE");
-#endif
 
-    size_t count = 0;
+    RRDSET *st = NULL;
+    uint32_t hash;
 
-    for(;;) {
+    errno = 0;
+    clearerr(fp);
+
+    if(unlikely(fileno(fp) == -1)) {
+        error("PLUGINSD: %s: file is not a valid stream.", cd->fullfilename);
+        goto cleanup;
+    }
+
+    while(!ferror(fp)) {
         if(unlikely(netdata_exit)) break;
 
-        FILE *fp = mypopen(cd->cmd, &cd->pid);
-        if(unlikely(!fp)) {
-            error("Cannot popen(\"%s\", \"r\").", cd->cmd);
+        char *r = fgets(line, PLUGINSD_LINE_MAX, fp);
+        if(unlikely(!r)) {
+            error("PLUGINSD: %s : read failed.", cd->fullfilename);
             break;
         }
 
-        info("PLUGINSD: '%s' running on pid %d", cd->fullfilename, cd->pid);
+        if(unlikely(netdata_exit)) break;
 
-        RRDSET *st = NULL;
-        uint32_t hash;
+        line[PLUGINSD_LINE_MAX] = '\0';
 
-        while(likely(fgets(line, PLUGINSD_LINE_MAX, fp) != NULL)) {
-            if(unlikely(netdata_exit)) break;
+        // debug(D_PLUGINSD, "PLUGINSD: %s: %s", cd->filename, line);
 
-            line[PLUGINSD_LINE_MAX] = '\0';
+        int w = pluginsd_split_words(line, words, MAX_WORDS);
+        char *s = words[0];
+        if(unlikely(!s || !*s || !w)) {
+            // debug(D_PLUGINSD, "PLUGINSD: empty line");
+            continue;
+        }
 
-            // debug(D_PLUGINSD, "PLUGINSD: %s: %s", cd->filename, line);
+        // debug(D_PLUGINSD, "PLUGINSD: words 0='%s' 1='%s' 2='%s' 3='%s' 4='%s' 5='%s' 6='%s' 7='%s' 8='%s' 9='%s'", words[0], words[1], words[2], words[3], words[4], words[5], words[6], words[7], words[8], words[9]);
 
-            int w = pluginsd_split_words(line, words, MAX_WORDS);
-            char *s = words[0];
-            if(unlikely(!s || !*s || !w)) {
-                // debug(D_PLUGINSD, "PLUGINSD: empty line");
-                continue;
-            }
+        if(likely(!simple_hash_strcmp(s, "SET", &hash))) {
+            char *dimension = words[1];
+            char *value = words[2];
 
-            // debug(D_PLUGINSD, "PLUGINSD: words 0='%s' 1='%s' 2='%s' 3='%s' 4='%s' 5='%s' 6='%s' 7='%s' 8='%s' 9='%s'", words[0], words[1], words[2], words[3], words[4], words[5], words[6], words[7], words[8], words[9]);
+            if(unlikely(!dimension || !*dimension)) {
+                error("PLUGINSD: '%s' is requesting a SET on chart '%s' of host '%s', without a dimension. Disabling it.", cd->fullfilename, st->id, host->hostname);
+                enabled = 0;
+                break;
+            }
 
-            if(likely(!simple_hash_strcmp(s, "SET", &hash))) {
-                char *dimension = words[1];
-                char *value = words[2];
+            if(unlikely(!value || !*value)) value = NULL;
 
-                if(unlikely(!dimension || !*dimension)) {
-                    error("PLUGINSD: '%s' is requesting a SET on chart '%s', without a dimension. Disabling it.", cd->fullfilename, st->id);
-                    cd->enabled = 0;
-                    killpid(cd->pid, SIGTERM);
-                    break;
-                }
+            if(unlikely(!st)) {
+                error("PLUGINSD: '%s' is requesting a SET on dimension %s with value %s on host '%s', without a BEGIN. Disabling it.", cd->fullfilename, dimension, value?value:"<nothing>", host->hostname);
+                enabled = 0;
+                break;
+            }
 
-                if(unlikely(!value || !*value)) value = NULL;
+            if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))) debug(D_PLUGINSD, "PLUGINSD: '%s' is setting dimension %s/%s to %s", cd->fullfilename, st->id, dimension, value?value:"<nothing>");
 
-                if(unlikely(!st)) {
-                    error("PLUGINSD: '%s' is requesting a SET on dimension %s with value %s, without a BEGIN. Disabling it.", cd->fullfilename, dimension, value?value:"<nothing>");
-                    cd->enabled = 0;
-                    killpid(cd->pid, SIGTERM);
-                    break;
-                }
-
-                if(unlikely(st->debug)) debug(D_PLUGINSD, "PLUGINSD: '%s' is setting dimension %s/%s to %s", cd->fullfilename, st->id, dimension, value?value:"<nothing>");
+            if(value) rrddim_set(st, dimension, strtoll(value, NULL, 0));
+        }
+        else if(likely(hash == BEGIN_HASH && !strcmp(s, "BEGIN"))) {
+            char *id = words[1];
+            char *microseconds_txt = words[2];
 
-                if(value) rrddim_set(st, dimension, strtoll(value, NULL, 0));
+            if(unlikely(!id)) {
+                error("PLUGINSD: '%s' is requesting a BEGIN without a chart id for host '%s'. Disabling it.", cd->fullfilename, host->hostname);
+                enabled = 0;
+                break;
             }
-            else if(likely(hash == BEGIN_HASH && !strcmp(s, "BEGIN"))) {
-                char *id = words[1];
-                char *microseconds_txt = words[2];
 
-                if(unlikely(!id)) {
-                    error("PLUGINSD: '%s' is requesting a BEGIN without a chart id. Disabling it.", cd->fullfilename);
-                    cd->enabled = 0;
-                    killpid(cd->pid, SIGTERM);
-                    break;
-                }
+            st = rrdset_find(host, id);
+            if(unlikely(!st)) {
+                error("PLUGINSD: '%s' is requesting a BEGIN on chart '%s', which does not exist on host '%s'. Disabling it.", cd->fullfilename, id, host->hostname);
+                enabled = 0;
+                break;
+            }
 
-                st = rrdset_find(id);
-                if(unlikely(!st)) {
-                    error("PLUGINSD: '%s' is requesting a BEGIN on chart '%s', which does not exist. Disabling it.", cd->fullfilename, id);
-                    cd->enabled = 0;
-                    killpid(cd->pid, SIGTERM);
-                    break;
-                }
+            if(likely(st->counter_done)) {
+                usec_t microseconds = 0;
+                if(microseconds_txt && *microseconds_txt) microseconds = str2ull(microseconds_txt);
 
-                if(likely(st->counter_done)) {
-                    usec_t microseconds = 0;
-                    if(microseconds_txt && *microseconds_txt) microseconds = str2ull(microseconds_txt);
-                    if(microseconds) rrdset_next_usec(st, microseconds);
-                    else rrdset_next(st);
+                if(likely(microseconds)) {
+                    if(trust_durations)
+                        rrdset_next_usec_unfiltered(st, microseconds);
+                    else
+                        rrdset_next_usec(st, microseconds);
                 }
+                else rrdset_next(st);
             }
-            else if(likely(hash == END_HASH && !strcmp(s, "END"))) {
-                if(unlikely(!st)) {
-                    error("PLUGINSD: '%s' is requesting an END, without a BEGIN. Disabling it.", cd->fullfilename);
-                    cd->enabled = 0;
-                    killpid(cd->pid, SIGTERM);
-                    break;
-                }
+        }
+        else if(likely(hash == END_HASH && !strcmp(s, "END"))) {
+            if(unlikely(!st)) {
+                error("PLUGINSD: '%s' is requesting an END, without a BEGIN on host '%s'. Disabling it.", cd->fullfilename, host->hostname);
+                enabled = 0;
+                break;
+            }
+
+            if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))) debug(D_PLUGINSD, "PLUGINSD: '%s' is requesting an END on chart %s", cd->fullfilename, st->id);
 
-                if(unlikely(st->debug)) debug(D_PLUGINSD, "PLUGINSD: '%s' is requesting an END on chart %s", cd->fullfilename, st->id);
+            rrdset_done(st);
+            st = NULL;
 
-                rrdset_done(st);
-                st = NULL;
+            count++;
+        }
+/*        else if(likely(hash == HOST_HASH && !strcmp(s, "HOST"))) {
+            char *guid = words[1];
+            char *hostname = words[2];
 
-                count++;
+            if(unlikely(!guid || !*guid)) {
+                error("PLUGINSD: '%s' is requesting HOST with guid '%s' and hostname '%s', without a guid. Disabling it.", cd->fullfilename, guid?guid:"", hostname?hostname:"");
+                enabled = 0;
+                break;
             }
-            else if(likely(hash == FLUSH_HASH && !strcmp(s, "FLUSH"))) {
-                debug(D_PLUGINSD, "PLUGINSD: '%s' is requesting a FLUSH", cd->fullfilename);
-                st = NULL;
+            if(unlikely(!hostname || !*hostname)) {
+                error("PLUGINSD: '%s' is requesting HOST with guid '%s' and hostname '%s', without a hostname. Disabling it.", cd->fullfilename, guid?guid:"", hostname?hostname:"");
+                enabled = 0;
+                break;
             }
-            else if(likely(hash == CHART_HASH && !strcmp(s, "CHART"))) {
-                int noname = 0;
-                st = NULL;
-
-                if((words[1]) != NULL && (words[2]) != NULL && strcmp(words[1], words[2]) == 0)
-                    noname = 1;
-
-                char *type = words[1];
-                char *id = NULL;
-                if(likely(type)) {
-                    id = strchr(type, '.');
-                    if(likely(id)) { *id = '\0'; id++; }
-                }
-                char *name = words[2];
-                char *title = words[3];
-                char *units = words[4];
-                char *family = words[5];
-                char *context = words[6];
-                char *chart = words[7];
-                char *priority_s = words[8];
-                char *update_every_s = words[9];
-
-                if(unlikely(!type || !*type || !id || !*id)) {
-                    error("PLUGINSD: '%s' is requesting a CHART, without a type.id. Disabling it.", cd->fullfilename);
-                    cd->enabled = 0;
-                    killpid(cd->pid, SIGTERM);
-                    break;
-                }
 
-                int priority = 1000;
-                if(likely(priority_s)) priority = str2i(priority_s);
-
-                int update_every = cd->update_every;
-                if(likely(update_every_s)) update_every = str2i(update_every_s);
-                if(unlikely(!update_every)) update_every = cd->update_every;
-
-                int chart_type = RRDSET_TYPE_LINE;
-                if(unlikely(chart)) chart_type = rrdset_type_id(chart);
-
-                if(unlikely(noname || !name || !*name || strcasecmp(name, "NULL") == 0 || strcasecmp(name, "(NULL)") == 0)) name = NULL;
-                if(unlikely(!family || !*family)) family = NULL;
-                if(unlikely(!context || !*context)) context = NULL;
-
-                st = rrdset_find_bytype(type, id);
-                if(unlikely(!st)) {
-                    debug(D_PLUGINSD, "PLUGINSD: Creating chart type='%s', id='%s', name='%s', family='%s', context='%s', chart='%s', priority=%d, update_every=%d"
-                        , type, id
-                        , name?name:""
-                        , family?family:""
-                        , context?context:""
-                        , rrdset_type_name(chart_type)
-                        , priority
-                        , update_every
-                        );
-
-                    st = rrdset_create(type, id, name, family, context, title, units, priority, update_every, chart_type);
-                    cd->update_every = update_every;
-                }
-                else debug(D_PLUGINSD, "PLUGINSD: Chart '%s' already exists. Not adding it again.", st->id);
+            host = rrdhost_find_or_create(hostname, guid);
+        } */
+        else if(likely(hash == FLUSH_HASH && !strcmp(s, "FLUSH"))) {
+            debug(D_PLUGINSD, "PLUGINSD: '%s' is requesting a FLUSH", cd->fullfilename);
+            st = NULL;
+        }
+        else if(likely(hash == CHART_HASH && !strcmp(s, "CHART"))) {
+            int noname = 0;
+            st = NULL;
+
+            if((words[1]) != NULL && (words[2]) != NULL && strcmp(words[1], words[2]) == 0)
+                noname = 1;
+
+            char *type = words[1];
+            char *id = NULL;
+            if(likely(type)) {
+                id = strchr(type, '.');
+                if(likely(id)) { *id = '\0'; id++; }
+            }
+            char *name = words[2];
+            char *title = words[3];
+            char *units = words[4];
+            char *family = words[5];
+            char *context = words[6];
+            char *chart = words[7];
+            char *priority_s = words[8];
+            char *update_every_s = words[9];
+
+            if(unlikely(!type || !*type || !id || !*id)) {
+                error("PLUGINSD: '%s' is requesting a CHART, without a type.id, on host '%s'. Disabling it.", cd->fullfilename, host->hostname);
+                enabled = 0;
+                break;
             }
-            else if(likely(hash == DIMENSION_HASH && !strcmp(s, "DIMENSION"))) {
-                char *id = words[1];
-                char *name = words[2];
-                char *algorithm = words[3];
-                char *multiplier_s = words[4];
-                char *divisor_s = words[5];
-                char *options = words[6];
-
-                if(unlikely(!id || !*id)) {
-                    error("PLUGINSD: '%s' is requesting a DIMENSION, without an id. Disabling it.", cd->fullfilename);
-                    cd->enabled = 0;
-                    killpid(cd->pid, SIGTERM);
-                    break;
-                }
-
-                if(unlikely(!st)) {
-                    error("PLUGINSD: '%s' is requesting a DIMENSION, without a CHART. Disabling it.", cd->fullfilename);
-                    cd->enabled = 0;
-                    killpid(cd->pid, SIGTERM);
-                    break;
-                }
 
-                long multiplier = 1;
-                if(multiplier_s && *multiplier_s) multiplier = strtol(multiplier_s, NULL, 0);
-                if(unlikely(!multiplier)) multiplier = 1;
-
-                long divisor = 1;
-                if(likely(divisor_s && *divisor_s)) divisor = strtol(divisor_s, NULL, 0);
-                if(unlikely(!divisor)) divisor = 1;
-
-                if(unlikely(!algorithm || !*algorithm)) algorithm = "absolute";
-
-                if(unlikely(st->debug)) debug(D_PLUGINSD, "PLUGINSD: Creating dimension in chart %s, id='%s', name='%s', algorithm='%s', multiplier=%ld, divisor=%ld, hidden='%s'"
-                    , st->id
-                    , id
-                    , name?name:""
-                    , rrddim_algorithm_name(rrddim_algorithm_id(algorithm))
-                    , multiplier
-                    , divisor
-                    , options?options:""
-                    );
-
-                RRDDIM *rd = rrddim_find(st, id);
-                if(unlikely(!rd)) {
-                    rd = rrddim_add(st, id, name, multiplier, divisor, rrddim_algorithm_id(algorithm));
-                    rd->flags = 0x00000000;
-                    if(options && *options) {
-                        if(strstr(options, "hidden") != NULL) rd->flags |= RRDDIM_FLAG_HIDDEN;
-                        if(strstr(options, "noreset") != NULL) rd->flags |= RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS;
-                        if(strstr(options, "nooverflow") != NULL) rd->flags |= RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS;
-                    }
-                }
-                else if(unlikely(st->debug)) debug(D_PLUGINSD, "PLUGINSD: dimension %s/%s already exists. Not adding it again.", st->id, id);
+            int priority = 1000;
+            if(likely(priority_s)) priority = str2i(priority_s);
+
+            int update_every = cd->update_every;
+            if(likely(update_every_s)) update_every = str2i(update_every_s);
+            if(unlikely(!update_every)) update_every = cd->update_every;
+
+            RRDSET_TYPE chart_type = RRDSET_TYPE_LINE;
+            if(unlikely(chart)) chart_type = rrdset_type_id(chart);
+
+            if(unlikely(noname || !name || !*name || strcasecmp(name, "NULL") == 0 || strcasecmp(name, "(NULL)") == 0)) name = NULL;
+            if(unlikely(!family || !*family)) family = NULL;
+            if(unlikely(!context || !*context)) context = NULL;
+
+            st = rrdset_find_bytype(host, type, id);
+            if(unlikely(!st)) {
+                debug(D_PLUGINSD, "PLUGINSD: Creating chart type='%s', id='%s', name='%s', family='%s', context='%s', chart='%s', priority=%d, update_every=%d"
+                      , type, id
+                      , name?name:""
+                      , family?family:""
+                      , context?context:""
+                      , rrdset_type_name(chart_type)
+                      , priority
+                      , update_every
+                );
+
+                st = rrdset_create(host, type, id, name, family, context, title, units, priority, update_every, chart_type);
+                cd->update_every = update_every;
             }
-            else if(unlikely(hash == DISABLE_HASH && !strcmp(s, "DISABLE"))) {
-                info("PLUGINSD: '%s' called DISABLE. Disabling it.", cd->fullfilename);
-                cd->enabled = 0;
-                killpid(cd->pid, SIGTERM);
+            else debug(D_PLUGINSD, "PLUGINSD: Chart '%s' already exists. Not adding it again.", st->id);
+        }
+        else if(likely(hash == DIMENSION_HASH && !strcmp(s, "DIMENSION"))) {
+            char *id = words[1];
+            char *name = words[2];
+            char *algorithm = words[3];
+            char *multiplier_s = words[4];
+            char *divisor_s = words[5];
+            char *options = words[6];
+
+            if(unlikely(!id || !*id)) {
+                error("PLUGINSD: '%s' is requesting a DIMENSION, without an id, host '%s' and chart '%s'. Disabling it.", cd->fullfilename, host->hostname, st?st->id:"UNSET");
+                enabled = 0;
                 break;
             }
-#ifdef DETACH_PLUGINS_FROM_NETDATA
-            else if(likely(hash == MYPID_HASH && !strcmp(s, "MYPID"))) {
-                char *pid_s = words[1];
-                pid_t pid = strtod(pid_s, NULL, 0);
 
-                if(likely(pid)) cd->pid = pid;
-                debug(D_PLUGINSD, "PLUGINSD: %s is on pid %d", cd->id, cd->pid);
+            if(unlikely(!st)) {
+                error("PLUGINSD: '%s' is requesting a DIMENSION, without a CHART, on host '%s'. Disabling it.", cd->fullfilename, host->hostname);
+                enabled = 0;
+                break;
             }
-            else if(likely(hash == STOPPING_WAKE_ME_UP_PLEASE_HASH && !strcmp(s, "STOPPING_WAKE_ME_UP_PLEASE"))) {
-                error("PLUGINSD: '%s' (pid %d) called STOPPING_WAKE_ME_UP_PLEASE.", cd->fullfilename, cd->pid);
 
-                now_realtime_timeval(&now);
-                if(unlikely(!usec && !susec)) {
-                    // our first run
-                    susec = cd->rrd_update_every * USEC_PER_SEC;
+            long multiplier = 1;
+            if(multiplier_s && *multiplier_s) multiplier = strtol(multiplier_s, NULL, 0);
+            if(unlikely(!multiplier)) multiplier = 1;
+
+            long divisor = 1;
+            if(likely(divisor_s && *divisor_s)) divisor = strtol(divisor_s, NULL, 0);
+            if(unlikely(!divisor)) divisor = 1;
+
+            if(unlikely(!algorithm || !*algorithm)) algorithm = "absolute";
+
+            if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+                debug(D_PLUGINSD, "PLUGINSD: Creating dimension in chart %s, id='%s', name='%s', algorithm='%s', multiplier=%ld, divisor=%ld, hidden='%s'"
+                      , st->id
+                      , id
+                      , name?name:""
+                      , rrd_algorithm_name(rrd_algorithm_id(algorithm))
+                      , multiplier
+                      , divisor
+                      , options?options:""
+                );
+
+            RRDDIM *rd = rrddim_find(st, id);
+            if(unlikely(!rd)) {
+                rd = rrddim_add(st, id, name, multiplier, divisor, rrd_algorithm_id(algorithm));
+                rrddim_flag_clear(rd, RRDDIM_FLAG_HIDDEN);
+                rrddim_flag_clear(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS);
+                if(options && *options) {
+                    if(strstr(options, "hidden") != NULL) rrddim_flag_set(rd, RRDDIM_FLAG_HIDDEN);
+                    if(strstr(options, "noreset") != NULL) rrddim_flag_set(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS);
+                    if(strstr(options, "nooverflow") != NULL) rrddim_flag_set(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS);
                 }
-                else {
-                    // second+ run
-                    usec = dt_usec(&now, &last) - susec;
-                    error("PLUGINSD: %s last loop took %llu usec (worked for %llu, sleeped for %llu).\n", cd->fullfilename, usec + susec, usec, susec);
-                    if(unlikely(usec < (rrd_update_every * USEC_PER_SEC / 2ULL))) susec = (rrd_update_every * USEC_PER_SEC) - usec;
-                    else susec = rrd_update_every * USEC_PER_SEC / 2ULL;
-                }
-
-                error("PLUGINSD: %s sleeping for %llu. Will kill with SIGCONT pid %d to wake it up.\n", cd->fullfilename, susec, cd->pid);
-                usleep(susec);
-                killpid(cd->pid, SIGCONT);
-                memmove(&last, &now, sizeof(struct timeval));
-                break;
-            }
-#endif
-            else {
-                error("PLUGINSD: '%s' is sending command '%s' which is not known by netdata. Disabling it.", cd->fullfilename, s);
-                cd->enabled = 0;
-                killpid(cd->pid, SIGTERM);
-                break;
             }
+            else if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+                debug(D_PLUGINSD, "PLUGINSD: dimension %s/%s already exists. Not adding it again.", st->id, id);
+        }
+        else if(unlikely(hash == DISABLE_HASH && !strcmp(s, "DISABLE"))) {
+            info("PLUGINSD: '%s' called DISABLE. Disabling it.", cd->fullfilename);
+            enabled = 0;
+            break;
         }
-        if(likely(count)) {
-            cd->successful_collections += count;
-            cd->serial_failures = 0;
+        else {
+            error("PLUGINSD: '%s' is sending command '%s' which is not known by netdata, for host '%s'. Disabling it.", cd->fullfilename, s, host->hostname);
+            enabled = 0;
+            break;
         }
-        else
-            cd->serial_failures++;
+    }
+
+cleanup:
+    cd->enabled = enabled;
+
+    if(likely(count)) {
+        cd->successful_collections += count;
+        cd->serial_failures = 0;
+    }
+    else
+        cd->serial_failures++;
+
+    return count;
+}
+
+void *pluginsd_worker_thread(void *arg) {
+    struct plugind *cd = (struct plugind *)arg;
+    cd->obsolete = 0;
+
+    size_t count = 0;
+
+    for(;;) {
+        if(unlikely(netdata_exit)) break;
+
+        FILE *fp = mypopen(cd->cmd, &cd->pid);
+        if(unlikely(!fp)) {
+            error("Cannot popen(\"%s\", \"r\").", cd->cmd);
+            break;
+        }
+
+        info("PLUGINSD: '%s' running on pid %d", cd->fullfilename, cd->pid);
+
+        count = pluginsd_process(localhost, cd, fp, 0);
+        error("PLUGINSD: plugin '%s' disconnected.", cd->fullfilename);
+
+        killpid(cd->pid, SIGTERM);
 
         info("PLUGINSD: '%s' on pid %d stopped after %zu successful data collections (ENDs).", cd->fullfilename, cd->pid, count);
 
         // get the return code
         int code = mypclose(fp, cd->pid);
-        
+
         if(unlikely(netdata_exit)) break;
         else if(code != 0) {
             // the plugin reports failure
@@ -442,14 +455,14 @@ void *pluginsd_main(void *ptr) {
     if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
         error("Cannot set pthread cancel state to ENABLE.");
 
-    int automatic_run = config_get_boolean("plugins", "enable running new plugins", 1);
-    int scan_frequency = (int) config_get_number("plugins", "check for new plugins every", 60);
+    int automatic_run = config_get_boolean(CONFIG_SECTION_PLUGINS, "enable running new plugins", 1);
+    int scan_frequency = (int) config_get_number(CONFIG_SECTION_PLUGINS, "check for new plugins every", 60);
     DIR *dir = NULL;
     struct dirent *file = NULL;
     struct plugind *cd;
 
     // enable the apps plugin by default
-    // config_get_boolean("plugins", "apps", 1);
+    // config_get_boolean(CONFIG_SECTION_PLUGINS, "apps", 1);
 
     if(scan_frequency < 1) scan_frequency = 1;
 
@@ -478,7 +491,7 @@ void *pluginsd_main(void *ptr) {
 
             char pluginname[CONFIG_MAX_NAME + 1];
             snprintfz(pluginname, CONFIG_MAX_NAME, "%.*s", (int)(len - PLUGINSD_FILE_SUFFIX_LEN), file->d_name);
-            int enabled = config_get_boolean("plugins", pluginname, automatic_run);
+            int enabled = config_get_boolean(CONFIG_SECTION_PLUGINS, pluginname, automatic_run);
 
             if(unlikely(!enabled)) {
                 debug(D_PLUGINSD, "PLUGINSD: plugin '%s' is not enabled", file->d_name);
@@ -505,7 +518,7 @@ void *pluginsd_main(void *ptr) {
                 snprintfz(cd->fullfilename, FILENAME_MAX, "%s/%s", netdata_configured_plugins_dir, cd->filename);
 
                 cd->enabled = enabled;
-                cd->update_every = (int) config_get_number(cd->id, "update every", rrd_update_every);
+                cd->update_every = (int) config_get_number(cd->id, "update every", localhost->rrd_update_every);
                 cd->started_t = now_realtime_sec();
 
                 char *def = "";
index 3c74355a3d6b30f6a7a93e2ba02e5898cdf4ad4e..d34c4030c55902b8814d92828e0be5c9faeb0f7d 100644 (file)
@@ -11,7 +11,7 @@ struct plugind {
 
     char filename[FILENAME_MAX+1];      // just the filename
     char fullfilename[FILENAME_MAX+1];  // with path
-    char cmd[PLUGINSD_CMD_MAX+1];       // the command that is executes
+    char cmd[PLUGINSD_CMD_MAX+1];       // the command that it executes
 
     pid_t pid;
     pthread_t thread;
@@ -34,5 +34,6 @@ struct plugind {
 extern struct plugind *pluginsd_root;
 
 extern void *pluginsd_main(void *ptr);
+extern size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int trust_durations);
 
 #endif /* NETDATA_PLUGINS_D_H */
index b21838b6182bdb682a43d2ef8b798eeba8613485..134e7a1a8d69aba290cc092e521b3a5110709305 100644 (file)
@@ -198,17 +198,17 @@ static inline int is_major_enabled(int major) {
 
 int do_proc_diskstats(int update_every, usec_t dt) {
     static procfile *ff = NULL;
-    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_ONDEMAND,
-                global_enable_performance_for_partitions = CONFIG_ONDEMAND_NO,
-                global_do_io = CONFIG_ONDEMAND_ONDEMAND,
-                global_do_ops = CONFIG_ONDEMAND_ONDEMAND,
-                global_do_mops = CONFIG_ONDEMAND_ONDEMAND,
-                global_do_iotime = CONFIG_ONDEMAND_ONDEMAND,
-                global_do_qops = CONFIG_ONDEMAND_ONDEMAND,
-                global_do_util = CONFIG_ONDEMAND_ONDEMAND,
-                global_do_backlog = CONFIG_ONDEMAND_ONDEMAND,
+    static int  global_enable_new_disks_detected_at_runtime = CONFIG_BOOLEAN_YES,
+                global_enable_performance_for_physical_disks = CONFIG_BOOLEAN_AUTO,
+                global_enable_performance_for_virtual_disks = CONFIG_BOOLEAN_AUTO,
+                global_enable_performance_for_partitions = CONFIG_BOOLEAN_NO,
+                global_do_io = CONFIG_BOOLEAN_AUTO,
+                global_do_ops = CONFIG_BOOLEAN_AUTO,
+                global_do_mops = CONFIG_BOOLEAN_AUTO,
+                global_do_iotime = CONFIG_BOOLEAN_AUTO,
+                global_do_qops = CONFIG_BOOLEAN_AUTO,
+                global_do_util = CONFIG_BOOLEAN_AUTO,
+                global_do_backlog = CONFIG_BOOLEAN_AUTO,
                 globals_initialized = 0;
 
     if(unlikely(!globals_initialized)) {
@@ -333,21 +333,21 @@ int do_proc_diskstats(int update_every, usec_t dt) {
             snprintfz(var_name, 4096, "plugin:proc:/proc/diskstats:%s", disk);
 
             int def_enable = config_get_boolean_ondemand(var_name, "enable", global_enable_new_disks_detected_at_runtime);
-            if(unlikely(def_enable == CONFIG_ONDEMAND_NO)) {
+            if(unlikely(def_enable == CONFIG_BOOLEAN_NO)) {
                 // the user does not want any metrics for this disk
-                d->do_io = CONFIG_ONDEMAND_NO;
-                d->do_ops = CONFIG_ONDEMAND_NO;
-                d->do_mops = CONFIG_ONDEMAND_NO;
-                d->do_iotime = CONFIG_ONDEMAND_NO;
-                d->do_qops = CONFIG_ONDEMAND_NO;
-                d->do_util = CONFIG_ONDEMAND_NO;
-                d->do_backlog = CONFIG_ONDEMAND_NO;
+                d->do_io = CONFIG_BOOLEAN_NO;
+                d->do_ops = CONFIG_BOOLEAN_NO;
+                d->do_mops = CONFIG_BOOLEAN_NO;
+                d->do_iotime = CONFIG_BOOLEAN_NO;
+                d->do_qops = CONFIG_BOOLEAN_NO;
+                d->do_util = CONFIG_BOOLEAN_NO;
+                d->do_backlog = CONFIG_BOOLEAN_NO;
             }
             else {
                 // this disk is enabled
                 // check its direct settings
 
-                int def_performance = CONFIG_ONDEMAND_ONDEMAND;
+                int def_performance = CONFIG_BOOLEAN_AUTO;
 
                 // since this is 'on demand' we can figure the performance settings
                 // based on the type of disk
@@ -378,16 +378,16 @@ int do_proc_diskstats(int update_every, usec_t dt) {
                 // check the user configuration (this will also show our 'on demand' decision)
                 def_performance = config_get_boolean_ondemand(var_name, "enable performance metrics", def_performance);
 
-                int ddo_io = CONFIG_ONDEMAND_NO,
-                    ddo_ops = CONFIG_ONDEMAND_NO,
-                    ddo_mops = CONFIG_ONDEMAND_NO,
-                    ddo_iotime = CONFIG_ONDEMAND_NO,
-                    ddo_qops = CONFIG_ONDEMAND_NO,
-                    ddo_util = CONFIG_ONDEMAND_NO,
-                    ddo_backlog = CONFIG_ONDEMAND_NO;
+                int ddo_io = CONFIG_BOOLEAN_NO,
+                    ddo_ops = CONFIG_BOOLEAN_NO,
+                    ddo_mops = CONFIG_BOOLEAN_NO,
+                    ddo_iotime = CONFIG_BOOLEAN_NO,
+                    ddo_qops = CONFIG_BOOLEAN_NO,
+                    ddo_util = CONFIG_BOOLEAN_NO,
+                    ddo_backlog = CONFIG_BOOLEAN_NO;
 
                 // we enable individual performance charts only when def_performance is not disabled
-                if(unlikely(def_performance != CONFIG_ONDEMAND_NO)) {
+                if(unlikely(def_performance != CONFIG_BOOLEAN_NO)) {
                     ddo_io = global_do_io,
                     ddo_ops = global_do_ops,
                     ddo_mops = global_do_mops,
@@ -414,15 +414,16 @@ int do_proc_diskstats(int update_every, usec_t dt) {
         // --------------------------------------------------------------------------
         // Do performance metrics
 
-        if(d->do_io == CONFIG_ONDEMAND_YES || (d->do_io == CONFIG_ONDEMAND_ONDEMAND && (readsectors || writesectors))) {
-            d->do_io = CONFIG_ONDEMAND_YES;
+        if(d->do_io == CONFIG_BOOLEAN_YES || (d->do_io == CONFIG_BOOLEAN_AUTO && (readsectors || writesectors))) {
+            d->do_io = CONFIG_BOOLEAN_YES;
 
-            st = rrdset_find_bytype(RRD_TYPE_DISK, disk);
+            st = rrdset_find_bytype_localhost(RRD_TYPE_DISK, disk);
             if(unlikely(!st)) {
-                st = rrdset_create(RRD_TYPE_DISK, disk, NULL, family, "disk.io", "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every, RRDSET_TYPE_AREA);
+                st = rrdset_create_localhost(RRD_TYPE_DISK, disk, NULL, family, "disk.io", "Disk I/O Bandwidth"
+                                             , "kilobytes/s", 2000, update_every, RRDSET_TYPE_AREA);
 
-                rrddim_add(st, "reads", NULL, d->sector_size, 1024, RRDDIM_INCREMENTAL);
-                rrddim_add(st, "writes", NULL, d->sector_size * -1, 1024, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "reads", NULL, d->sector_size, 1024, RRD_ALGORITHM_INCREMENTAL);
+                rrddim_add(st, "writes", NULL, d->sector_size * -1, 1024, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(st);
 
@@ -433,16 +434,17 @@ int do_proc_diskstats(int update_every, usec_t dt) {
 
         // --------------------------------------------------------------------
 
-        if(d->do_ops == CONFIG_ONDEMAND_YES || (d->do_ops == CONFIG_ONDEMAND_ONDEMAND && (reads || writes))) {
-            d->do_ops = CONFIG_ONDEMAND_YES;
+        if(d->do_ops == CONFIG_BOOLEAN_YES || (d->do_ops == CONFIG_BOOLEAN_AUTO && (reads || writes))) {
+            d->do_ops = CONFIG_BOOLEAN_YES;
 
-            st = rrdset_find_bytype("disk_ops", disk);
+            st = rrdset_find_bytype_localhost("disk_ops", disk);
             if(unlikely(!st)) {
-                st = rrdset_create("disk_ops", disk, NULL, family, "disk.ops", "Disk Completed I/O Operations", "operations/s", 2001, update_every, RRDSET_TYPE_LINE);
-                st->isdetail = 1;
+                st = rrdset_create_localhost("disk_ops", disk, NULL, family, "disk.ops", "Disk Completed I/O Operations"
+                                             , "operations/s", 2001, update_every, RRDSET_TYPE_LINE);
+                rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "reads", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                rrddim_add(st, "writes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(st);
 
@@ -453,15 +455,16 @@ int do_proc_diskstats(int update_every, usec_t dt) {
 
         // --------------------------------------------------------------------
 
-        if(d->do_qops == CONFIG_ONDEMAND_YES || (d->do_qops == CONFIG_ONDEMAND_ONDEMAND && queued_ios)) {
-            d->do_qops = CONFIG_ONDEMAND_YES;
+        if(d->do_qops == CONFIG_BOOLEAN_YES || (d->do_qops == CONFIG_BOOLEAN_AUTO && queued_ios)) {
+            d->do_qops = CONFIG_BOOLEAN_YES;
 
-            st = rrdset_find_bytype("disk_qops", disk);
+            st = rrdset_find_bytype_localhost("disk_qops", disk);
             if(unlikely(!st)) {
-                st = rrdset_create("disk_qops", disk, NULL, family, "disk.qops", "Disk Current I/O Operations", "operations", 2002, update_every, RRDSET_TYPE_LINE);
-                st->isdetail = 1;
+                st = rrdset_create_localhost("disk_qops", disk, NULL, family, "disk.qops", "Disk Current I/O Operations"
+                                             , "operations", 2002, update_every, RRDSET_TYPE_LINE);
+                rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                rrddim_add(st, "operations", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                rrddim_add(st, "operations", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
             }
             else rrdset_next(st);
 
@@ -471,15 +474,16 @@ int do_proc_diskstats(int update_every, usec_t dt) {
 
         // --------------------------------------------------------------------
 
-        if(d->do_backlog == CONFIG_ONDEMAND_YES || (d->do_backlog == CONFIG_ONDEMAND_ONDEMAND && backlog_ms)) {
-            d->do_backlog = CONFIG_ONDEMAND_YES;
+        if(d->do_backlog == CONFIG_BOOLEAN_YES || (d->do_backlog == CONFIG_BOOLEAN_AUTO && backlog_ms)) {
+            d->do_backlog = CONFIG_BOOLEAN_YES;
 
-            st = rrdset_find_bytype("disk_backlog", disk);
+            st = rrdset_find_bytype_localhost("disk_backlog", disk);
             if(unlikely(!st)) {
-                st = rrdset_create("disk_backlog", disk, NULL, family, "disk.backlog", "Disk Backlog", "backlog (ms)", 2003, update_every, RRDSET_TYPE_AREA);
-                st->isdetail = 1;
+                st = rrdset_create_localhost("disk_backlog", disk, NULL, family, "disk.backlog", "Disk Backlog"
+                                             , "backlog (ms)", 2003, update_every, RRDSET_TYPE_AREA);
+                rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                rrddim_add(st, "backlog", NULL, 1, 10, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "backlog", NULL, 1, 10, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(st);
 
@@ -489,15 +493,16 @@ int do_proc_diskstats(int update_every, usec_t dt) {
 
         // --------------------------------------------------------------------
 
-        if(d->do_util == CONFIG_ONDEMAND_YES || (d->do_util == CONFIG_ONDEMAND_ONDEMAND && busy_ms)) {
-            d->do_util = CONFIG_ONDEMAND_YES;
+        if(d->do_util == CONFIG_BOOLEAN_YES || (d->do_util == CONFIG_BOOLEAN_AUTO && busy_ms)) {
+            d->do_util = CONFIG_BOOLEAN_YES;
 
-            st = rrdset_find_bytype("disk_util", disk);
+            st = rrdset_find_bytype_localhost("disk_util", disk);
             if(unlikely(!st)) {
-                st = rrdset_create("disk_util", disk, NULL, family, "disk.util", "Disk Utilization Time", "% of time working", 2004, update_every, RRDSET_TYPE_AREA);
-                st->isdetail = 1;
+                st = rrdset_create_localhost("disk_util", disk, NULL, family, "disk.util", "Disk Utilization Time"
+                                             , "% of time working", 2004, update_every, RRDSET_TYPE_AREA);
+                rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                rrddim_add(st, "utilization", NULL, 1, 10, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "utilization", NULL, 1, 10, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(st);
 
@@ -507,16 +512,17 @@ int do_proc_diskstats(int update_every, usec_t dt) {
 
         // --------------------------------------------------------------------
 
-        if(d->do_mops == CONFIG_ONDEMAND_YES || (d->do_mops == CONFIG_ONDEMAND_ONDEMAND && (mreads || mwrites))) {
-            d->do_mops = CONFIG_ONDEMAND_YES;
+        if(d->do_mops == CONFIG_BOOLEAN_YES || (d->do_mops == CONFIG_BOOLEAN_AUTO && (mreads || mwrites))) {
+            d->do_mops = CONFIG_BOOLEAN_YES;
 
-            st = rrdset_find_bytype("disk_mops", disk);
+            st = rrdset_find_bytype_localhost("disk_mops", disk);
             if(unlikely(!st)) {
-                st = rrdset_create("disk_mops", disk, NULL, family, "disk.mops", "Disk Merged Operations", "merged operations/s", 2021, update_every, RRDSET_TYPE_LINE);
-                st->isdetail = 1;
+                st = rrdset_create_localhost("disk_mops", disk, NULL, family, "disk.mops", "Disk Merged Operations"
+                                             , "merged operations/s", 2021, update_every, RRDSET_TYPE_LINE);
+                rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "reads", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                rrddim_add(st, "writes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(st);
 
@@ -527,16 +533,17 @@ int do_proc_diskstats(int update_every, usec_t dt) {
 
         // --------------------------------------------------------------------
 
-        if(d->do_iotime == CONFIG_ONDEMAND_YES || (d->do_iotime == CONFIG_ONDEMAND_ONDEMAND && (readms || writems))) {
-            d->do_iotime = CONFIG_ONDEMAND_YES;
+        if(d->do_iotime == CONFIG_BOOLEAN_YES || (d->do_iotime == CONFIG_BOOLEAN_AUTO && (readms || writems))) {
+            d->do_iotime = CONFIG_BOOLEAN_YES;
 
-            st = rrdset_find_bytype("disk_iotime", disk);
+            st = rrdset_find_bytype_localhost("disk_iotime", disk);
             if(unlikely(!st)) {
-                st = rrdset_create("disk_iotime", disk, NULL, family, "disk.iotime", "Disk Total I/O Time", "milliseconds/s", 2022, update_every, RRDSET_TYPE_LINE);
-                st->isdetail = 1;
+                st = rrdset_create_localhost("disk_iotime", disk, NULL, family, "disk.iotime", "Disk Total I/O Time"
+                                             , "milliseconds/s", 2022, update_every, RRDSET_TYPE_LINE);
+                rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "reads", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                rrddim_add(st, "writes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(st);
 
@@ -550,15 +557,17 @@ int do_proc_diskstats(int update_every, usec_t dt) {
         // only if this is not the first time we run
 
         if(likely(dt)) {
-            if( (d->do_iotime == CONFIG_ONDEMAND_YES || (d->do_iotime == CONFIG_ONDEMAND_ONDEMAND && (readms || writems))) &&
-                (d->do_ops    == CONFIG_ONDEMAND_YES || (d->do_ops    == CONFIG_ONDEMAND_ONDEMAND && (reads || writes)))) {
-                st = rrdset_find_bytype("disk_await", disk);
+            if( (d->do_iotime == CONFIG_BOOLEAN_YES || (d->do_iotime == CONFIG_BOOLEAN_AUTO && (readms || writems))) &&
+                (d->do_ops    == CONFIG_BOOLEAN_YES || (d->do_ops    == CONFIG_BOOLEAN_AUTO && (reads || writes)))) {
+                st = rrdset_find_bytype_localhost("disk_await", disk);
                 if(unlikely(!st)) {
-                    st = rrdset_create("disk_await", disk, NULL, family, "disk.await", "Average Completed I/O Operation Time", "ms per operation", 2005, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("disk_await", disk, NULL, family, "disk.await"
+                                                 , "Average Completed I/O Operation Time", "ms per operation", 2005
+                                                 , update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_ABSOLUTE);
-                    rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_ABSOLUTE);
+                    rrddim_add(st, "reads", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+                    rrddim_add(st, "writes", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE);
                 }
                 else rrdset_next(st);
 
@@ -567,15 +576,17 @@ int do_proc_diskstats(int update_every, usec_t dt) {
                 rrdset_done(st);
             }
 
-            if( (d->do_io  == CONFIG_ONDEMAND_YES || (d->do_io  == CONFIG_ONDEMAND_ONDEMAND && (readsectors || writesectors))) &&
-                (d->do_ops == CONFIG_ONDEMAND_YES || (d->do_ops == CONFIG_ONDEMAND_ONDEMAND && (reads || writes)))) {
-                st = rrdset_find_bytype("disk_avgsz", disk);
+            if( (d->do_io  == CONFIG_BOOLEAN_YES || (d->do_io  == CONFIG_BOOLEAN_AUTO && (readsectors || writesectors))) &&
+                (d->do_ops == CONFIG_BOOLEAN_YES || (d->do_ops == CONFIG_BOOLEAN_AUTO && (reads || writes)))) {
+                st = rrdset_find_bytype_localhost("disk_avgsz", disk);
                 if(unlikely(!st)) {
-                    st = rrdset_create("disk_avgsz", disk, NULL, family, "disk.avgsz", "Average Completed I/O Operation Bandwidth", "kilobytes per operation", 2006, update_every, RRDSET_TYPE_AREA);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("disk_avgsz", disk, NULL, family, "disk.avgsz"
+                                                 , "Average Completed I/O Operation Bandwidth"
+                                                 , "kilobytes per operation", 2006, update_every, RRDSET_TYPE_AREA);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "reads", NULL, d->sector_size, 1024, RRDDIM_ABSOLUTE);
-                    rrddim_add(st, "writes", NULL, d->sector_size * -1, 1024, RRDDIM_ABSOLUTE);
+                    rrddim_add(st, "reads", NULL, d->sector_size, 1024, RRD_ALGORITHM_ABSOLUTE);
+                    rrddim_add(st, "writes", NULL, d->sector_size * -1, 1024, RRD_ALGORITHM_ABSOLUTE);
                 }
                 else rrdset_next(st);
 
@@ -584,14 +595,15 @@ int do_proc_diskstats(int update_every, usec_t dt) {
                 rrdset_done(st);
             }
 
-            if( (d->do_util == CONFIG_ONDEMAND_YES || (d->do_util == CONFIG_ONDEMAND_ONDEMAND && busy_ms)) &&
-                (d->do_ops  == CONFIG_ONDEMAND_YES || (d->do_ops  == CONFIG_ONDEMAND_ONDEMAND && (reads || writes)))) {
-                st = rrdset_find_bytype("disk_svctm", disk);
+            if( (d->do_util == CONFIG_BOOLEAN_YES || (d->do_util == CONFIG_BOOLEAN_AUTO && busy_ms)) &&
+                (d->do_ops  == CONFIG_BOOLEAN_YES || (d->do_ops  == CONFIG_BOOLEAN_AUTO && (reads || writes)))) {
+                st = rrdset_find_bytype_localhost("disk_svctm", disk);
                 if(unlikely(!st)) {
-                    st = rrdset_create("disk_svctm", disk, NULL, family, "disk.svctm", "Average Service Time", "ms per operation", 2007, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("disk_svctm", disk, NULL, family, "disk.svctm", "Average Service Time"
+                                                 , "ms per operation", 2007, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "svctm", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                    rrddim_add(st, "svctm", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
                 }
                 else rrdset_next(st);
 
index 5e29f4f07b0348c8c30dcfd1bdd12572ac2e72e5..082e1f57b9c59233202d13834ae7fd49a5ff745a 100644 (file)
@@ -146,8 +146,9 @@ int do_proc_interrupts(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    st = rrdset_find_bytype("system", "interrupts");
-    if(unlikely(!st)) st = rrdset_create("system", "interrupts", NULL, "interrupts", NULL, "System interrupts", "interrupts/s", 1000, update_every, RRDSET_TYPE_STACKED);
+    st = rrdset_find_bytype_localhost("system", "interrupts");
+    if(unlikely(!st)) st = rrdset_create_localhost("system", "interrupts", NULL, "interrupts", NULL, "System interrupts"
+                                                   , "interrupts/s", 1000, update_every, RRDSET_TYPE_STACKED);
     else rrdset_next(st);
 
     for(l = 0; l < lines ;l++) {
@@ -159,7 +160,7 @@ int do_proc_interrupts(int update_every, usec_t dt) {
         if(unlikely(!irr->rd || strncmp(irr->rd->name, irr->name, MAX_INTERRUPT_NAME) != 0)) {
             irr->rd = rrddim_find(st, irr->id);
             if(unlikely(!irr->rd))
-                irr->rd = rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL);
+                irr->rd = rrddim_add(st, irr->id, irr->name, 1, 1, RRD_ALGORITHM_INCREMENTAL);
             else
                 rrddim_set_name(st, irr->rd, irr->name);
 
@@ -181,11 +182,12 @@ int do_proc_interrupts(int update_every, usec_t dt) {
             char id[50+1];
             snprintfz(id, 50, "cpu%d_interrupts", c);
 
-            st = rrdset_find_bytype("cpu", id);
+            st = rrdset_find_bytype_localhost("cpu", id);
             if(unlikely(!st)) {
                 char title[100+1];
                 snprintfz(title, 100, "CPU%d Interrupts", c);
-                st = rrdset_create("cpu", id, NULL, "interrupts", "cpu.interrupts", title, "interrupts/s", 1100 + c, update_every, RRDSET_TYPE_STACKED);
+                st = rrdset_create_localhost("cpu", id, NULL, "interrupts", "cpu.interrupts", title, "interrupts/s",
+                        1100 + c, update_every, RRDSET_TYPE_STACKED);
             }
             else rrdset_next(st);
 
@@ -195,7 +197,7 @@ int do_proc_interrupts(int update_every, usec_t dt) {
                 if(unlikely(!irr->cpu[c].rd)) {
                     irr->cpu[c].rd = rrddim_find(st, irr->id);
                     if(unlikely(!irr->cpu[c].rd))
-                        irr->cpu[c].rd = rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL);
+                        irr->cpu[c].rd = rrddim_add(st, irr->id, irr->name, 1, 1, RRD_ALGORITHM_INCREMENTAL);
                     else
                         rrddim_set_name(st, irr->cpu[c].rd, irr->name);
                 }
index 4b1779f70c1f9ad8058f0641cae9c5114750192e..e7863f114a7445bf6594e4270b3095139260c91b 100644 (file)
@@ -50,12 +50,15 @@ int do_proc_loadavg(int update_every, usec_t dt) {
     if(next_loadavg_dt <= dt) {
         if(likely(do_loadavg)) {
             if(unlikely(!load_chart)) {
-                load_chart = rrdset_find_byname("system.load");
+                load_chart = rrdset_find_byname_localhost("system.load");
                 if(unlikely(!load_chart)) {
-                    load_chart = rrdset_create("system", "load", NULL, "load", NULL, "System Load Average", "load", 100, (update_every < MIN_LOADAVG_UPDATE_EVERY) ? MIN_LOADAVG_UPDATE_EVERY : update_every, RRDSET_TYPE_LINE);
-                    rrddim_add(load_chart, "load1", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-                    rrddim_add(load_chart, "load5", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-                    rrddim_add(load_chart, "load15", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+                    load_chart = rrdset_create_localhost("system", "load", NULL, "load", NULL, "System Load Average"
+                                                         , "load", 100, (update_every < MIN_LOADAVG_UPDATE_EVERY)
+                                                                        ? MIN_LOADAVG_UPDATE_EVERY : update_every
+                                                         , RRDSET_TYPE_LINE);
+                    rrddim_add(load_chart, "load1", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
+                    rrddim_add(load_chart, "load5", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
+                    rrddim_add(load_chart, "load15", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
                 }
             }
             else
@@ -75,10 +78,12 @@ int do_proc_loadavg(int update_every, usec_t dt) {
 
     if(likely(do_all_processes)) {
         if(unlikely(!processes_chart)) {
-            processes_chart = rrdset_find_byname("system.active_processes");
+            processes_chart = rrdset_find_byname_localhost("system.active_processes");
             if(unlikely(!processes_chart)) {
-                processes_chart = rrdset_create("system", "active_processes", NULL, "processes", NULL, "System Active Processes", "processes", 750, update_every, RRDSET_TYPE_LINE);
-                rrddim_add(processes_chart, "active", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                processes_chart = rrdset_create_localhost("system", "active_processes", NULL, "processes", NULL
+                                                          , "System Active Processes", "processes", 750, update_every
+                                                          , RRDSET_TYPE_LINE);
+                rrddim_add(processes_chart, "active", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
             }
         }
         else rrdset_next(processes_chart);
index da6a430b93654b1af3324165cf66fb980fb876bb..6b0219cc910e0e520a45703d04e8542601f195d8 100644 (file)
@@ -55,8 +55,8 @@ int do_proc_meminfo(int update_every, usec_t dt) {
 
     if(unlikely(!arl_base)) {
         do_ram          = config_get_boolean("plugin:proc:/proc/meminfo", "system ram", 1);
-        do_swap         = config_get_boolean_ondemand("plugin:proc:/proc/meminfo", "system swap", CONFIG_ONDEMAND_ONDEMAND);
-        do_hwcorrupt    = config_get_boolean_ondemand("plugin:proc:/proc/meminfo", "hardware corrupted ECC", CONFIG_ONDEMAND_ONDEMAND);
+        do_swap         = config_get_boolean_ondemand("plugin:proc:/proc/meminfo", "system swap", CONFIG_BOOLEAN_AUTO);
+        do_hwcorrupt    = config_get_boolean_ondemand("plugin:proc:/proc/meminfo", "hardware corrupted ECC", CONFIG_BOOLEAN_AUTO);
         do_committed    = config_get_boolean("plugin:proc:/proc/meminfo", "committed memory", 1);
         do_writeback    = config_get_boolean("plugin:proc:/proc/meminfo", "writeback memory", 1);
         do_kernel       = config_get_boolean("plugin:proc:/proc/meminfo", "kernel memory", 1);
@@ -140,14 +140,15 @@ int do_proc_meminfo(int update_every, usec_t dt) {
     unsigned long long MemUsed = MemTotal - MemFree - Cached - Buffers;
 
     if(do_ram) {
-        st = rrdset_find("system.ram");
+        st = rrdset_find_localhost("system.ram");
         if(!st) {
-            st = rrdset_create("system", "ram", NULL, "ram", NULL, "System RAM", "MB", 200, update_every, RRDSET_TYPE_STACKED);
+            st = rrdset_create_localhost("system", "ram", NULL, "ram", NULL, "System RAM", "MB", 200, update_every
+                                         , RRDSET_TYPE_STACKED);
 
-            rrddim_add(st, "free",    NULL, 1, 1024, RRDDIM_ABSOLUTE);
-            rrddim_add(st, "used",    NULL, 1, 1024, RRDDIM_ABSOLUTE);
-            rrddim_add(st, "cached",  NULL, 1, 1024, RRDDIM_ABSOLUTE);
-            rrddim_add(st, "buffers", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "free",    NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
+            rrddim_add(st, "used",    NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
+            rrddim_add(st, "cached",  NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
+            rrddim_add(st, "buffers", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
         }
         else rrdset_next(st);
 
@@ -162,16 +163,17 @@ int do_proc_meminfo(int update_every, usec_t dt) {
 
     unsigned long long SwapUsed = SwapTotal - SwapFree;
 
-    if(SwapTotal || SwapUsed || SwapFree || do_swap == CONFIG_ONDEMAND_YES) {
-        do_swap = CONFIG_ONDEMAND_YES;
+    if(SwapTotal || SwapUsed || SwapFree || do_swap == CONFIG_BOOLEAN_YES) {
+        do_swap = CONFIG_BOOLEAN_YES;
 
-        st = rrdset_find("system.swap");
+        st = rrdset_find_localhost("system.swap");
         if(!st) {
-            st = rrdset_create("system", "swap", NULL, "swap", NULL, "System Swap", "MB", 201, update_every, RRDSET_TYPE_STACKED);
-            st->isdetail = 1;
+            st = rrdset_create_localhost("system", "swap", NULL, "swap", NULL, "System Swap", "MB", 201, update_every
+                                         , RRDSET_TYPE_STACKED);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-            rrddim_add(st, "free",    NULL, 1, 1024, RRDDIM_ABSOLUTE);
-            rrddim_add(st, "used",    NULL, 1, 1024, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "free",    NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
+            rrddim_add(st, "used",    NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
         }
         else rrdset_next(st);
 
@@ -182,15 +184,16 @@ int do_proc_meminfo(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(arl_hwcorrupted->flags & ARL_ENTRY_FLAG_FOUND && (do_hwcorrupt == CONFIG_ONDEMAND_YES || (do_hwcorrupt == CONFIG_ONDEMAND_ONDEMAND && HardwareCorrupted > 0))) {
-        do_hwcorrupt = CONFIG_ONDEMAND_YES;
+    if(arl_hwcorrupted->flags & ARL_ENTRY_FLAG_FOUND && (do_hwcorrupt == CONFIG_BOOLEAN_YES || (do_hwcorrupt == CONFIG_BOOLEAN_AUTO && HardwareCorrupted > 0))) {
+        do_hwcorrupt = CONFIG_BOOLEAN_YES;
 
-        st = rrdset_find("mem.hwcorrupt");
+        st = rrdset_find_localhost("mem.hwcorrupt");
         if(!st) {
-            st = rrdset_create("mem", "hwcorrupt", NULL, "ecc", NULL, "Hardware Corrupted ECC", "MB", 9000, update_every, RRDSET_TYPE_LINE);
-            st->isdetail = 1;
+            st = rrdset_create_localhost("mem", "hwcorrupt", NULL, "ecc", NULL, "Hardware Corrupted ECC", "MB", 9000
+                                         , update_every, RRDSET_TYPE_LINE);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-            rrddim_add(st, "HardwareCorrupted", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "HardwareCorrupted", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
         }
         else rrdset_next(st);
 
@@ -201,12 +204,13 @@ int do_proc_meminfo(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(do_committed) {
-        st = rrdset_find("mem.committed");
+        st = rrdset_find_localhost("mem.committed");
         if(!st) {
-            st = rrdset_create("mem", "committed", NULL, "system", NULL, "Committed (Allocated) Memory", "MB", 5000, update_every, RRDSET_TYPE_AREA);
-            st->isdetail = 1;
+            st = rrdset_create_localhost("mem", "committed", NULL, "system", NULL, "Committed (Allocated) Memory", "MB"
+                                         , 5000, update_every, RRDSET_TYPE_AREA);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-            rrddim_add(st, "Committed_AS", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "Committed_AS", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
         }
         else rrdset_next(st);
 
@@ -217,16 +221,17 @@ int do_proc_meminfo(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(do_writeback) {
-        st = rrdset_find("mem.writeback");
+        st = rrdset_find_localhost("mem.writeback");
         if(!st) {
-            st = rrdset_create("mem", "writeback", NULL, "kernel", NULL, "Writeback Memory", "MB", 4000, update_every, RRDSET_TYPE_LINE);
-            st->isdetail = 1;
-
-            rrddim_add(st, "Dirty", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-            rrddim_add(st, "Writeback", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-            rrddim_add(st, "FuseWriteback", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-            rrddim_add(st, "NfsWriteback", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-            rrddim_add(st, "Bounce", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+            st = rrdset_create_localhost("mem", "writeback", NULL, "kernel", NULL, "Writeback Memory", "MB", 4000
+                                         , update_every, RRDSET_TYPE_LINE);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+            rrddim_add(st, "Dirty", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
+            rrddim_add(st, "Writeback", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
+            rrddim_add(st, "FuseWriteback", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
+            rrddim_add(st, "NfsWriteback", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
+            rrddim_add(st, "Bounce", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
         }
         else rrdset_next(st);
 
@@ -241,15 +246,16 @@ int do_proc_meminfo(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(do_kernel) {
-        st = rrdset_find("mem.kernel");
+        st = rrdset_find_localhost("mem.kernel");
         if(!st) {
-            st = rrdset_create("mem", "kernel", NULL, "kernel", NULL, "Memory Used by Kernel", "MB", 6000, update_every, RRDSET_TYPE_STACKED);
-            st->isdetail = 1;
-
-            rrddim_add(st, "Slab", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-            rrddim_add(st, "KernelStack", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-            rrddim_add(st, "PageTables", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-            rrddim_add(st, "VmallocUsed", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+            st = rrdset_create_localhost("mem", "kernel", NULL, "kernel", NULL, "Memory Used by Kernel", "MB", 6000
+                                         , update_every, RRDSET_TYPE_STACKED);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+            rrddim_add(st, "Slab", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
+            rrddim_add(st, "KernelStack", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
+            rrddim_add(st, "PageTables", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
+            rrddim_add(st, "VmallocUsed", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
         }
         else rrdset_next(st);
 
@@ -263,13 +269,14 @@ int do_proc_meminfo(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(do_slab) {
-        st = rrdset_find("mem.slab");
+        st = rrdset_find_localhost("mem.slab");
         if(!st) {
-            st = rrdset_create("mem", "slab", NULL, "slab", NULL, "Reclaimable Kernel Memory", "MB", 6500, update_every, RRDSET_TYPE_STACKED);
-            st->isdetail = 1;
+            st = rrdset_create_localhost("mem", "slab", NULL, "slab", NULL, "Reclaimable Kernel Memory", "MB", 6500
+                                         , update_every, RRDSET_TYPE_STACKED);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-            rrddim_add(st, "reclaimable", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-            rrddim_add(st, "unreclaimable", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "reclaimable", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
+            rrddim_add(st, "unreclaimable", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
         }
         else rrdset_next(st);
 
index bb1e4c0fac1b387e075da17b918a2be1865a4d98..9b942326240baf710e9848d705b1635c3c8bcf92 100644 (file)
@@ -117,15 +117,15 @@ int do_proc_net_dev(int update_every, usec_t dt) {
     static int do_bandwidth = -1, do_packets = -1, do_errors = -1, do_drops = -1, do_fifo = -1, do_compressed = -1, do_events = -1;
 
     if(unlikely(enable_new_interfaces == -1)) {
-        enable_new_interfaces = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "enable new interfaces detected at runtime", CONFIG_ONDEMAND_ONDEMAND);
+        enable_new_interfaces = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "enable new interfaces detected at runtime", CONFIG_BOOLEAN_AUTO);
 
-        do_bandwidth    = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "bandwidth for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
-        do_packets      = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "packets for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
-        do_errors       = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "errors for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
-        do_drops        = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "drops for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
-        do_fifo         = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "fifo for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
-        do_compressed   = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "compressed packets for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
-        do_events       = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "frames, collisions, carrier counters for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
+        do_bandwidth    = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "bandwidth for all interfaces", CONFIG_BOOLEAN_AUTO);
+        do_packets      = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "packets for all interfaces", CONFIG_BOOLEAN_AUTO);
+        do_errors       = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "errors for all interfaces", CONFIG_BOOLEAN_AUTO);
+        do_drops        = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "drops for all interfaces", CONFIG_BOOLEAN_AUTO);
+        do_fifo         = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "fifo for all interfaces", CONFIG_BOOLEAN_AUTO);
+        do_compressed   = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "compressed packets for all interfaces", CONFIG_BOOLEAN_AUTO);
+        do_events       = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "frames, collisions, carrier counters for all interfaces", CONFIG_BOOLEAN_AUTO);
 
         disabled_list = simple_pattern_create(
                 config_get("plugin:proc:/proc/net/dev", "disable by default interfaces matching", "lo fireqos* *-ifb")
@@ -164,7 +164,7 @@ int do_proc_net_dev(int update_every, usec_t dt) {
             snprintfz(var_name, 512, "plugin:proc:/proc/net/dev:%s", d->name);
             d->enabled = config_get_boolean_ondemand(var_name, "enabled", d->enabled);
 
-            if(d->enabled == CONFIG_ONDEMAND_NO)
+            if(d->enabled == CONFIG_BOOLEAN_NO)
                 continue;
 
             d->do_bandwidth  = config_get_boolean_ondemand(var_name, "bandwidth", do_bandwidth);
@@ -179,38 +179,38 @@ int do_proc_net_dev(int update_every, usec_t dt) {
         if(unlikely(!d->enabled))
             continue;
 
-        if(likely(d->do_bandwidth != CONFIG_ONDEMAND_NO)) {
+        if(likely(d->do_bandwidth != CONFIG_BOOLEAN_NO)) {
             d->rbytes      = str2kernel_uint_t(procfile_lineword(ff, l, 1));
             d->tbytes      = str2kernel_uint_t(procfile_lineword(ff, l, 9));
         }
 
-        if(likely(d->do_packets != CONFIG_ONDEMAND_NO)) {
+        if(likely(d->do_packets != CONFIG_BOOLEAN_NO)) {
             d->rpackets    = str2kernel_uint_t(procfile_lineword(ff, l, 2));
             d->rmulticast  = str2kernel_uint_t(procfile_lineword(ff, l, 8));
             d->tpackets    = str2kernel_uint_t(procfile_lineword(ff, l, 10));
         }
 
-        if(likely(d->do_errors != CONFIG_ONDEMAND_NO)) {
+        if(likely(d->do_errors != CONFIG_BOOLEAN_NO)) {
             d->rerrors     = str2kernel_uint_t(procfile_lineword(ff, l, 3));
             d->terrors     = str2kernel_uint_t(procfile_lineword(ff, l, 11));
         }
 
-        if(likely(d->do_drops != CONFIG_ONDEMAND_NO)) {
+        if(likely(d->do_drops != CONFIG_BOOLEAN_NO)) {
             d->rdrops      = str2kernel_uint_t(procfile_lineword(ff, l, 4));
             d->tdrops      = str2kernel_uint_t(procfile_lineword(ff, l, 12));
         }
 
-        if(likely(d->do_fifo != CONFIG_ONDEMAND_NO)) {
+        if(likely(d->do_fifo != CONFIG_BOOLEAN_NO)) {
             d->rfifo       = str2kernel_uint_t(procfile_lineword(ff, l, 5));
             d->tfifo       = str2kernel_uint_t(procfile_lineword(ff, l, 13));
         }
 
-        if(likely(d->do_compressed != CONFIG_ONDEMAND_NO)) {
+        if(likely(d->do_compressed != CONFIG_BOOLEAN_NO)) {
             d->rcompressed = str2kernel_uint_t(procfile_lineword(ff, l, 7));
             d->tcompressed = str2kernel_uint_t(procfile_lineword(ff, l, 16));
         }
 
-        if(likely(d->do_events != CONFIG_ONDEMAND_NO)) {
+        if(likely(d->do_events != CONFIG_BOOLEAN_NO)) {
             d->rframe      = str2kernel_uint_t(procfile_lineword(ff, l, 6));
             d->tcollisions = str2kernel_uint_t(procfile_lineword(ff, l, 14));
             d->tcarrier    = str2kernel_uint_t(procfile_lineword(ff, l, 15));
@@ -218,18 +218,19 @@ int do_proc_net_dev(int update_every, usec_t dt) {
 
         // --------------------------------------------------------------------
 
-        if(unlikely((d->do_bandwidth == CONFIG_ONDEMAND_ONDEMAND && (d->rbytes || d->tbytes))))
-            d->do_bandwidth = CONFIG_ONDEMAND_YES;
+        if(unlikely((d->do_bandwidth == CONFIG_BOOLEAN_AUTO && (d->rbytes || d->tbytes))))
+            d->do_bandwidth = CONFIG_BOOLEAN_YES;
 
-        if(d->do_bandwidth == CONFIG_ONDEMAND_YES) {
+        if(d->do_bandwidth == CONFIG_BOOLEAN_YES) {
             if(unlikely(!d->st_bandwidth)) {
-                d->st_bandwidth = rrdset_find_bytype("net", d->name);
+                d->st_bandwidth = rrdset_find_bytype_localhost("net", d->name);
 
                 if(!d->st_bandwidth)
-                    d->st_bandwidth = rrdset_create("net", d->name, NULL, d->name, "net.net", "Bandwidth", "kilobits/s", 7000, update_every, RRDSET_TYPE_AREA);
+                    d->st_bandwidth = rrdset_create_localhost("net", d->name, NULL, d->name, "net.net", "Bandwidth"
+                                                              , "kilobits/s", 7000, update_every, RRDSET_TYPE_AREA);
 
-                d->rd_rbytes = rrddim_add(d->st_bandwidth, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
-                d->rd_tbytes = rrddim_add(d->st_bandwidth, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+                d->rd_rbytes = rrddim_add(d->st_bandwidth, "received", NULL, 8, 1024, RRD_ALGORITHM_INCREMENTAL);
+                d->rd_tbytes = rrddim_add(d->st_bandwidth, "sent", NULL, -8, 1024, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(d->st_bandwidth);
 
@@ -240,21 +241,22 @@ int do_proc_net_dev(int update_every, usec_t dt) {
 
         // --------------------------------------------------------------------
 
-        if(unlikely((d->do_packets == CONFIG_ONDEMAND_ONDEMAND && (d->rpackets || d->tpackets || d->rmulticast))))
-            d->do_packets = CONFIG_ONDEMAND_YES;
+        if(unlikely((d->do_packets == CONFIG_BOOLEAN_AUTO && (d->rpackets || d->tpackets || d->rmulticast))))
+            d->do_packets = CONFIG_BOOLEAN_YES;
 
-        if(d->do_packets == CONFIG_ONDEMAND_YES) {
+        if(d->do_packets == CONFIG_BOOLEAN_YES) {
             if(unlikely(!d->st_packets)) {
-                d->st_packets = rrdset_find_bytype("net_packets", d->name);
+                d->st_packets = rrdset_find_bytype_localhost("net_packets", d->name);
 
                 if(!d->st_packets)
-                    d->st_packets = rrdset_create("net_packets", d->name, NULL, d->name, "net.packets", "Packets", "packets/s", 7001, update_every, RRDSET_TYPE_LINE);
-
-                d->st_packets->isdetail = 1;
-
-                d->rd_rpackets = rrddim_add(d->st_packets, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                d->rd_tpackets = rrddim_add(d->st_packets, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                d->rd_rmulticast = rrddim_add(d->st_packets, "multicast", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    d->st_packets = rrdset_create_localhost("net_packets", d->name, NULL, d->name, "net.packets"
+                                                            , "Packets", "packets/s", 7001, update_every
+                                                            , RRDSET_TYPE_LINE);
+                rrdset_flag_set(d->st_packets, RRDSET_FLAG_DETAIL);
+
+                d->rd_rpackets = rrddim_add(d->st_packets, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                d->rd_tpackets = rrddim_add(d->st_packets, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                d->rd_rmulticast = rrddim_add(d->st_packets, "multicast", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(d->st_packets);
 
@@ -266,20 +268,22 @@ int do_proc_net_dev(int update_every, usec_t dt) {
 
         // --------------------------------------------------------------------
 
-        if(unlikely((d->do_errors == CONFIG_ONDEMAND_ONDEMAND && (d->rerrors || d->terrors))))
-            d->do_errors = CONFIG_ONDEMAND_YES;
+        if(unlikely((d->do_errors == CONFIG_BOOLEAN_AUTO && (d->rerrors || d->terrors))))
+            d->do_errors = CONFIG_BOOLEAN_YES;
 
-        if(d->do_errors == CONFIG_ONDEMAND_YES) {
+        if(d->do_errors == CONFIG_BOOLEAN_YES) {
             if(unlikely(!d->st_errors)) {
-                d->st_errors = rrdset_find_bytype("net_errors", d->name);
+                d->st_errors = rrdset_find_bytype_localhost("net_errors", d->name);
 
                 if(!d->st_errors)
-                    d->st_errors = rrdset_create("net_errors", d->name, NULL, d->name, "net.errors", "Interface Errors", "errors/s", 7002, update_every, RRDSET_TYPE_LINE);
+                    d->st_errors = rrdset_create_localhost("net_errors", d->name, NULL, d->name, "net.errors"
+                                                           , "Interface Errors", "errors/s", 7002, update_every
+                                                           , RRDSET_TYPE_LINE);
 
-                d->st_errors->isdetail = 1;
+                rrdset_flag_set(d->st_errors, RRDSET_FLAG_DETAIL);
 
-                d->rd_rerrors = rrddim_add(d->st_errors, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                d->rd_terrors = rrddim_add(d->st_errors, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                d->rd_rerrors = rrddim_add(d->st_errors, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                d->rd_terrors = rrddim_add(d->st_errors, "outbound", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(d->st_errors);
 
@@ -290,20 +294,22 @@ int do_proc_net_dev(int update_every, usec_t dt) {
 
         // --------------------------------------------------------------------
 
-        if(unlikely((d->do_drops == CONFIG_ONDEMAND_ONDEMAND && (d->rdrops || d->tdrops))))
-            d->do_drops = CONFIG_ONDEMAND_YES;
+        if(unlikely((d->do_drops == CONFIG_BOOLEAN_AUTO && (d->rdrops || d->tdrops))))
+            d->do_drops = CONFIG_BOOLEAN_YES;
 
-        if(d->do_drops == CONFIG_ONDEMAND_YES) {
+        if(d->do_drops == CONFIG_BOOLEAN_YES) {
             if(unlikely(!d->st_drops)) {
-                d->st_drops = rrdset_find_bytype("net_drops", d->name);
+                d->st_drops = rrdset_find_bytype_localhost("net_drops", d->name);
 
                 if(!d->st_drops)
-                    d->st_drops = rrdset_create("net_drops", d->name, NULL, d->name, "net.drops", "Interface Drops", "drops/s", 7003, update_every, RRDSET_TYPE_LINE);
+                    d->st_drops = rrdset_create_localhost("net_drops", d->name, NULL, d->name, "net.drops"
+                                                          , "Interface Drops", "drops/s", 7003, update_every
+                                                          , RRDSET_TYPE_LINE);
 
-                d->st_drops->isdetail = 1;
+                rrdset_flag_set(d->st_drops, RRDSET_FLAG_DETAIL);
 
-                d->rd_rdrops = rrddim_add(d->st_drops, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                d->rd_tdrops = rrddim_add(d->st_drops, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                d->rd_rdrops = rrddim_add(d->st_drops, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                d->rd_tdrops = rrddim_add(d->st_drops, "outbound", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(d->st_drops);
 
@@ -314,20 +320,22 @@ int do_proc_net_dev(int update_every, usec_t dt) {
 
         // --------------------------------------------------------------------
 
-        if(unlikely((d->do_fifo == CONFIG_ONDEMAND_ONDEMAND && (d->rfifo || d->tfifo))))
-            d->do_fifo = CONFIG_ONDEMAND_YES;
+        if(unlikely((d->do_fifo == CONFIG_BOOLEAN_AUTO && (d->rfifo || d->tfifo))))
+            d->do_fifo = CONFIG_BOOLEAN_YES;
 
-        if(d->do_fifo == CONFIG_ONDEMAND_YES) {
+        if(d->do_fifo == CONFIG_BOOLEAN_YES) {
             if(unlikely(!d->st_fifo)) {
-                d->st_fifo = rrdset_find_bytype("net_fifo", d->name);
+                d->st_fifo = rrdset_find_bytype_localhost("net_fifo", d->name);
 
                 if(!d->st_fifo)
-                    d->st_fifo = rrdset_create("net_fifo", d->name, NULL, d->name, "net.fifo", "Interface FIFO Buffer Errors", "errors", 7004, update_every, RRDSET_TYPE_LINE);
+                    d->st_fifo = rrdset_create_localhost("net_fifo", d->name, NULL, d->name, "net.fifo"
+                                                         , "Interface FIFO Buffer Errors", "errors", 7004, update_every
+                                                         , RRDSET_TYPE_LINE);
 
-                d->st_fifo->isdetail = 1;
+                rrdset_flag_set(d->st_fifo, RRDSET_FLAG_DETAIL);
 
-                d->rd_rfifo = rrddim_add(d->st_fifo, "receive", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                d->rd_tfifo = rrddim_add(d->st_fifo, "transmit", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                d->rd_rfifo = rrddim_add(d->st_fifo, "receive", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                d->rd_tfifo = rrddim_add(d->st_fifo, "transmit", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(d->st_fifo);
 
@@ -338,19 +346,21 @@ int do_proc_net_dev(int update_every, usec_t dt) {
 
         // --------------------------------------------------------------------
 
-        if(unlikely((d->do_compressed == CONFIG_ONDEMAND_ONDEMAND && (d->rcompressed || d->tcompressed))))
-            d->do_compressed = CONFIG_ONDEMAND_YES;
+        if(unlikely((d->do_compressed == CONFIG_BOOLEAN_AUTO && (d->rcompressed || d->tcompressed))))
+            d->do_compressed = CONFIG_BOOLEAN_YES;
 
-        if(d->do_compressed == CONFIG_ONDEMAND_YES) {
+        if(d->do_compressed == CONFIG_BOOLEAN_YES) {
             if(unlikely(!d->st_compressed)) {
-                d->st_compressed = rrdset_find_bytype("net_compressed", d->name);
+                d->st_compressed = rrdset_find_bytype_localhost("net_compressed", d->name);
                 if(!d->st_compressed)
-                    d->st_compressed = rrdset_create("net_compressed", d->name, NULL, d->name, "net.compressed", "Compressed Packets", "packets/s", 7005, update_every, RRDSET_TYPE_LINE);
+                    d->st_compressed = rrdset_create_localhost("net_compressed", d->name, NULL, d->name
+                                                               , "net.compressed", "Compressed Packets", "packets/s"
+                                                               , 7005, update_every, RRDSET_TYPE_LINE);
 
-                d->st_compressed->isdetail = 1;
+                rrdset_flag_set(d->st_compressed, RRDSET_FLAG_DETAIL);
 
-                d->rd_rcompressed = rrddim_add(d->st_compressed, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                d->rd_tcompressed = rrddim_add(d->st_compressed, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                d->rd_rcompressed = rrddim_add(d->st_compressed, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                d->rd_tcompressed = rrddim_add(d->st_compressed, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(d->st_compressed);
 
@@ -361,20 +371,22 @@ int do_proc_net_dev(int update_every, usec_t dt) {
 
         // --------------------------------------------------------------------
 
-        if(unlikely((d->do_events == CONFIG_ONDEMAND_ONDEMAND && (d->rframe || d->tcollisions || d->tcarrier))))
-            d->do_events = CONFIG_ONDEMAND_YES;
+        if(unlikely((d->do_events == CONFIG_BOOLEAN_AUTO && (d->rframe || d->tcollisions || d->tcarrier))))
+            d->do_events = CONFIG_BOOLEAN_YES;
 
-        if(d->do_events == CONFIG_ONDEMAND_YES) {
+        if(d->do_events == CONFIG_BOOLEAN_YES) {
             if(unlikely(!d->st_events)) {
-                d->st_events = rrdset_find_bytype("net_events", d->name);
+                d->st_events = rrdset_find_bytype_localhost("net_events", d->name);
                 if(!d->st_events)
-                    d->st_events = rrdset_create("net_events", d->name, NULL, d->name, "net.events", "Network Interface Events", "events/s", 7006, update_every, RRDSET_TYPE_LINE);
+                    d->st_events = rrdset_create_localhost("net_events", d->name, NULL, d->name, "net.events"
+                                                           , "Network Interface Events", "events/s", 7006, update_every
+                                                           , RRDSET_TYPE_LINE);
 
-                d->st_events->isdetail = 1;
+                rrdset_flag_set(d->st_events, RRDSET_FLAG_DETAIL);
 
-                d->rd_rframe      = rrddim_add(d->st_events, "frames", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                d->rd_tcollisions = rrddim_add(d->st_events, "collisions", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                d->rd_tcarrier    = rrddim_add(d->st_events, "carrier", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                d->rd_rframe      = rrddim_add(d->st_events, "frames", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                d->rd_tcollisions = rrddim_add(d->st_events, "collisions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                d->rd_tcarrier    = rrddim_add(d->st_events, "carrier", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(d->st_events);
 
index 96f4660f65dcf9de7d40227c63ea97a3dece9627..16a3234dff4fe5c1b7918bc11e4cc40999d2cce7 100644 (file)
@@ -40,11 +40,13 @@ int do_proc_net_ip_vs_stats(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(do_sockets) {
-        st = rrdset_find(RRD_TYPE_NET_IPVS ".sockets");
+        st = rrdset_find_localhost(RRD_TYPE_NET_IPVS ".sockets");
         if(!st) {
-            st = rrdset_create(RRD_TYPE_NET_IPVS, "sockets", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS New Connections", "connections/s", 3101, update_every, RRDSET_TYPE_LINE);
+            st = rrdset_create_localhost(RRD_TYPE_NET_IPVS, "sockets", NULL, RRD_TYPE_NET_IPVS, NULL
+                                         , "IPVS New Connections", "connections/s", 3101, update_every
+                                         , RRDSET_TYPE_LINE);
 
-            rrddim_add(st, "connections", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "connections", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -55,12 +57,13 @@ int do_proc_net_ip_vs_stats(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(do_packets) {
-        st = rrdset_find(RRD_TYPE_NET_IPVS ".packets");
+        st = rrdset_find_localhost(RRD_TYPE_NET_IPVS ".packets");
         if(!st) {
-            st = rrdset_create(RRD_TYPE_NET_IPVS, "packets", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS Packets", "packets/s", 3102, update_every, RRDSET_TYPE_LINE);
+            st = rrdset_create_localhost(RRD_TYPE_NET_IPVS, "packets", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS Packets"
+                                         , "packets/s", 3102, update_every, RRDSET_TYPE_LINE);
 
-            rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -72,12 +75,13 @@ int do_proc_net_ip_vs_stats(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(do_bandwidth) {
-        st = rrdset_find(RRD_TYPE_NET_IPVS ".net");
+        st = rrdset_find_localhost(RRD_TYPE_NET_IPVS ".net");
         if(!st) {
-            st = rrdset_create(RRD_TYPE_NET_IPVS, "net", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS Bandwidth", "kilobits/s", 3100, update_every, RRDSET_TYPE_AREA);
+            st = rrdset_create_localhost(RRD_TYPE_NET_IPVS, "net", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS Bandwidth"
+                                         , "kilobits/s", 3100, update_every, RRDSET_TYPE_AREA);
 
-            rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "received", NULL, 8, 1024, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -8, 1024, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
index 2235e79b0251bb3408b84c4c62b9fff1c9e53f9d..2677a6c17b3433c7a153297743c3defe4399ea9e 100644 (file)
@@ -98,19 +98,19 @@ int do_proc_net_netstat(int update_every, usec_t dt) {
         hash_ipext = simple_hash("IpExt");
         hash_tcpext = simple_hash("TcpExt");
 
-        do_bandwidth = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "bandwidth", CONFIG_ONDEMAND_ONDEMAND);
-        do_inerrors  = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "input errors", CONFIG_ONDEMAND_ONDEMAND);
-        do_mcast     = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "multicast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
-        do_bcast     = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "broadcast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
-        do_mcast_p   = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "multicast packets", CONFIG_ONDEMAND_ONDEMAND);
-        do_bcast_p   = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "broadcast packets", CONFIG_ONDEMAND_ONDEMAND);
-        do_ecn       = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "ECN packets", CONFIG_ONDEMAND_ONDEMAND);
-
-        do_tcpext_reorder    = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP reorders", CONFIG_ONDEMAND_ONDEMAND);
-        do_tcpext_syscookies = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP SYN cookies", CONFIG_ONDEMAND_ONDEMAND);
-        do_tcpext_ofo        = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP out-of-order queue", CONFIG_ONDEMAND_ONDEMAND);
-        do_tcpext_connaborts = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP connection aborts", CONFIG_ONDEMAND_ONDEMAND);
-        do_tcpext_memory     = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP memory pressures", CONFIG_ONDEMAND_ONDEMAND);
+        do_bandwidth = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "bandwidth", CONFIG_BOOLEAN_AUTO);
+        do_inerrors  = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "input errors", CONFIG_BOOLEAN_AUTO);
+        do_mcast     = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "multicast bandwidth", CONFIG_BOOLEAN_AUTO);
+        do_bcast     = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "broadcast bandwidth", CONFIG_BOOLEAN_AUTO);
+        do_mcast_p   = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "multicast packets", CONFIG_BOOLEAN_AUTO);
+        do_bcast_p   = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "broadcast packets", CONFIG_BOOLEAN_AUTO);
+        do_ecn       = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "ECN packets", CONFIG_BOOLEAN_AUTO);
+
+        do_tcpext_reorder    = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP reorders", CONFIG_BOOLEAN_AUTO);
+        do_tcpext_syscookies = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP SYN cookies", CONFIG_BOOLEAN_AUTO);
+        do_tcpext_ofo        = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP out-of-order queue", CONFIG_BOOLEAN_AUTO);
+        do_tcpext_connaborts = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP connection aborts", CONFIG_BOOLEAN_AUTO);
+        do_tcpext_memory     = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP memory pressures", CONFIG_BOOLEAN_AUTO);
 
         arl_ipext  = arl_create("netstat/ipext", NULL, 60);
         arl_tcpext = arl_create("netstat/tcpext", NULL, 60);
@@ -118,38 +118,38 @@ int do_proc_net_netstat(int update_every, usec_t dt) {
         // --------------------------------------------------------------------
         // IPv4
 
-        if(do_bandwidth != CONFIG_ONDEMAND_NO) {
+        if(do_bandwidth != CONFIG_BOOLEAN_NO) {
             arl_expect(arl_ipext, "InOctets",  &ipext_InOctets);
             arl_expect(arl_ipext, "OutOctets", &ipext_OutOctets);
         }
 
-        if(do_inerrors != CONFIG_ONDEMAND_NO) {
+        if(do_inerrors != CONFIG_BOOLEAN_NO) {
             arl_expect(arl_ipext, "InNoRoutes",      &ipext_InNoRoutes);
             arl_expect(arl_ipext, "InTruncatedPkts", &ipext_InTruncatedPkts);
             arl_expect(arl_ipext, "InCsumErrors",    &ipext_InCsumErrors);
         }
 
-        if(do_mcast != CONFIG_ONDEMAND_NO) {
+        if(do_mcast != CONFIG_BOOLEAN_NO) {
             arl_expect(arl_ipext, "InMcastOctets", &ipext_InMcastOctets);
             arl_expect(arl_ipext, "OutMcastOctets", &ipext_OutMcastOctets);
         }
 
-        if(do_mcast_p != CONFIG_ONDEMAND_NO) {
+        if(do_mcast_p != CONFIG_BOOLEAN_NO) {
             arl_expect(arl_ipext, "InMcastPkts",  &ipext_InMcastPkts);
             arl_expect(arl_ipext, "OutMcastPkts", &ipext_OutMcastPkts);
         }
 
-        if(do_bcast != CONFIG_ONDEMAND_NO) {
+        if(do_bcast != CONFIG_BOOLEAN_NO) {
             arl_expect(arl_ipext, "InBcastPkts",  &ipext_InBcastPkts);
             arl_expect(arl_ipext, "OutBcastPkts", &ipext_OutBcastPkts);
         }
 
-        if(do_bcast_p != CONFIG_ONDEMAND_NO) {
+        if(do_bcast_p != CONFIG_BOOLEAN_NO) {
             arl_expect(arl_ipext, "InBcastOctets",  &ipext_InBcastOctets);
             arl_expect(arl_ipext, "OutBcastOctets", &ipext_OutBcastOctets);
         }
 
-        if(do_ecn != CONFIG_ONDEMAND_NO) {
+        if(do_ecn != CONFIG_BOOLEAN_NO) {
             arl_expect(arl_ipext, "InNoECTPkts", &ipext_InNoECTPkts);
             arl_expect(arl_ipext, "InECT1Pkts",  &ipext_InECT1Pkts);
             arl_expect(arl_ipext, "InECT0Pkts",  &ipext_InECT0Pkts);
@@ -159,27 +159,27 @@ int do_proc_net_netstat(int update_every, usec_t dt) {
         // --------------------------------------------------------------------
         // IPv4 TCP
 
-        if(do_tcpext_reorder != CONFIG_ONDEMAND_NO) {
+        if(do_tcpext_reorder != CONFIG_BOOLEAN_NO) {
             arl_expect(arl_tcpext, "TCPFACKReorder", &tcpext_TCPFACKReorder);
             arl_expect(arl_tcpext, "TCPSACKReorder", &tcpext_TCPSACKReorder);
             arl_expect(arl_tcpext, "TCPRenoReorder", &tcpext_TCPRenoReorder);
             arl_expect(arl_tcpext, "TCPTSReorder",   &tcpext_TCPTSReorder);
         }
 
-        if(do_tcpext_syscookies != CONFIG_ONDEMAND_NO) {
+        if(do_tcpext_syscookies != CONFIG_BOOLEAN_NO) {
             arl_expect(arl_tcpext, "SyncookiesSent",   &tcpext_SyncookiesSent);
             arl_expect(arl_tcpext, "SyncookiesRecv",   &tcpext_SyncookiesRecv);
             arl_expect(arl_tcpext, "SyncookiesFailed", &tcpext_SyncookiesFailed);
         }
 
-        if(do_tcpext_ofo != CONFIG_ONDEMAND_NO) {
+        if(do_tcpext_ofo != CONFIG_BOOLEAN_NO) {
             arl_expect(arl_tcpext, "TCPOFOQueue", &tcpext_TCPOFOQueue);
             arl_expect(arl_tcpext, "TCPOFODrop",  &tcpext_TCPOFODrop);
             arl_expect(arl_tcpext, "TCPOFOMerge", &tcpext_TCPOFOMerge);
             arl_expect(arl_tcpext, "OfoPruned",   &tcpext_OfoPruned);
         }
 
-        if(do_tcpext_connaborts != CONFIG_ONDEMAND_NO) {
+        if(do_tcpext_connaborts != CONFIG_BOOLEAN_NO) {
             arl_expect(arl_tcpext, "TCPAbortOnData",    &tcpext_TCPAbortOnData);
             arl_expect(arl_tcpext, "TCPAbortOnClose",   &tcpext_TCPAbortOnClose);
             arl_expect(arl_tcpext, "TCPAbortOnMemory",  &tcpext_TCPAbortOnMemory);
@@ -188,7 +188,7 @@ int do_proc_net_netstat(int update_every, usec_t dt) {
             arl_expect(arl_tcpext, "TCPAbortFailed",    &tcpext_TCPAbortFailed);
         }
 
-        if(do_tcpext_memory != CONFIG_ONDEMAND_NO) {
+        if(do_tcpext_memory != CONFIG_BOOLEAN_NO) {
             arl_expect(arl_tcpext, "TCPMemoryPressures", &tcpext_TCPMemoryPressures);
         }
     }
@@ -228,14 +228,15 @@ int do_proc_net_netstat(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if(do_bandwidth == CONFIG_ONDEMAND_YES || (do_bandwidth == CONFIG_ONDEMAND_ONDEMAND && (ipext_InOctets || ipext_OutOctets))) {
-                do_bandwidth = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("system.ipv4");
+            if(do_bandwidth == CONFIG_BOOLEAN_YES || (do_bandwidth == CONFIG_BOOLEAN_AUTO && (ipext_InOctets || ipext_OutOctets))) {
+                do_bandwidth = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("system.ipv4");
                 if(unlikely(!st)) {
-                    st = rrdset_create("system", "ipv4", NULL, "network", NULL, "IPv4 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA);
+                    st = rrdset_create_localhost("system", "ipv4", NULL, "network", NULL, "IPv4 Bandwidth", "kilobits/s"
+                                                 , 500, update_every, RRDSET_TYPE_AREA);
 
-                    rrddim_add(st, "InOctets", "received", 8, 1024, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutOctets", "sent", -8, 1024, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InOctets", "received", 8, 1024, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutOctets", "sent", -8, 1024, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -246,16 +247,17 @@ int do_proc_net_netstat(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if(do_inerrors == CONFIG_ONDEMAND_YES || (do_inerrors == CONFIG_ONDEMAND_ONDEMAND && (ipext_InNoRoutes || ipext_InTruncatedPkts))) {
-                do_inerrors = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv4.inerrors");
+            if(do_inerrors == CONFIG_BOOLEAN_YES || (do_inerrors == CONFIG_BOOLEAN_AUTO && (ipext_InNoRoutes || ipext_InTruncatedPkts))) {
+                do_inerrors = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv4.inerrors");
                 if(unlikely(!st)) {
-                    st = rrdset_create("ipv4", "inerrors", NULL, "errors", NULL, "IPv4 Input Errors", "packets/s", 4000, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("ipv4", "inerrors", NULL, "errors", NULL, "IPv4 Input Errors"
+                                                 , "packets/s", 4000, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "InNoRoutes", "noroutes", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InTruncatedPkts", "truncated", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InCsumErrors", "checksum", 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InNoRoutes", "noroutes", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InTruncatedPkts", "truncated", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InCsumErrors", "checksum", 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -267,15 +269,16 @@ int do_proc_net_netstat(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if(do_mcast == CONFIG_ONDEMAND_YES || (do_mcast == CONFIG_ONDEMAND_ONDEMAND && (ipext_InMcastOctets || ipext_OutMcastOctets))) {
-                do_mcast = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv4.mcast");
+            if(do_mcast == CONFIG_BOOLEAN_YES || (do_mcast == CONFIG_BOOLEAN_AUTO && (ipext_InMcastOctets || ipext_OutMcastOctets))) {
+                do_mcast = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv4.mcast");
                 if(unlikely(!st)) {
-                    st = rrdset_create("ipv4", "mcast", NULL, "multicast", NULL, "IPv4 Multicast Bandwidth", "kilobits/s", 9000, update_every, RRDSET_TYPE_AREA);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("ipv4", "mcast", NULL, "multicast", NULL, "IPv4 Multicast Bandwidth"
+                                                 , "kilobits/s", 9000, update_every, RRDSET_TYPE_AREA);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "InMcastOctets", "received", 8, 1024, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutMcastOctets", "sent", -8, 1024, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InMcastOctets", "received", 8, 1024, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutMcastOctets", "sent", -8, 1024, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -286,15 +289,16 @@ int do_proc_net_netstat(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if(do_bcast == CONFIG_ONDEMAND_YES || (do_bcast == CONFIG_ONDEMAND_ONDEMAND && (ipext_InBcastOctets || ipext_OutBcastOctets))) {
-                do_bcast = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv4.bcast");
+            if(do_bcast == CONFIG_BOOLEAN_YES || (do_bcast == CONFIG_BOOLEAN_AUTO && (ipext_InBcastOctets || ipext_OutBcastOctets))) {
+                do_bcast = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv4.bcast");
                 if(unlikely(!st)) {
-                    st = rrdset_create("ipv4", "bcast", NULL, "broadcast", NULL, "IPv4 Broadcast Bandwidth", "kilobits/s", 8000, update_every, RRDSET_TYPE_AREA);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("ipv4", "bcast", NULL, "broadcast", NULL, "IPv4 Broadcast Bandwidth"
+                                                 , "kilobits/s", 8000, update_every, RRDSET_TYPE_AREA);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "InBcastOctets", "received", 8, 1024, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutBcastOctets", "sent", -8, 1024, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InBcastOctets", "received", 8, 1024, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutBcastOctets", "sent", -8, 1024, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -305,15 +309,16 @@ int do_proc_net_netstat(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if(do_mcast_p == CONFIG_ONDEMAND_YES || (do_mcast_p == CONFIG_ONDEMAND_ONDEMAND && (ipext_InMcastPkts || ipext_OutMcastPkts))) {
-                do_mcast_p = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv4.mcastpkts");
+            if(do_mcast_p == CONFIG_BOOLEAN_YES || (do_mcast_p == CONFIG_BOOLEAN_AUTO && (ipext_InMcastPkts || ipext_OutMcastPkts))) {
+                do_mcast_p = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv4.mcastpkts");
                 if(unlikely(!st)) {
-                    st = rrdset_create("ipv4", "mcastpkts", NULL, "multicast", NULL, "IPv4 Multicast Packets", "packets/s", 8600, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("ipv4", "mcastpkts", NULL, "multicast", NULL, "IPv4 Multicast Packets"
+                                                 , "packets/s", 8600, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "InMcastPkts", "received", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutMcastPkts", "sent", -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InMcastPkts", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutMcastPkts", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -324,15 +329,16 @@ int do_proc_net_netstat(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if(do_bcast_p == CONFIG_ONDEMAND_YES || (do_bcast_p == CONFIG_ONDEMAND_ONDEMAND && (ipext_InBcastPkts || ipext_OutBcastPkts))) {
-                do_bcast_p = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv4.bcastpkts");
+            if(do_bcast_p == CONFIG_BOOLEAN_YES || (do_bcast_p == CONFIG_BOOLEAN_AUTO && (ipext_InBcastPkts || ipext_OutBcastPkts))) {
+                do_bcast_p = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv4.bcastpkts");
                 if(unlikely(!st)) {
-                    st = rrdset_create("ipv4", "bcastpkts", NULL, "broadcast", NULL, "IPv4 Broadcast Packets", "packets/s", 8500, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("ipv4", "bcastpkts", NULL, "broadcast", NULL, "IPv4 Broadcast Packets"
+                                                 , "packets/s", 8500, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "InBcastPkts", "received", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutBcastPkts", "sent", -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InBcastPkts", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutBcastPkts", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -343,17 +349,18 @@ int do_proc_net_netstat(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if(do_ecn == CONFIG_ONDEMAND_YES || (do_ecn == CONFIG_ONDEMAND_ONDEMAND && (ipext_InCEPkts || ipext_InECT0Pkts || ipext_InECT1Pkts || ipext_InNoECTPkts))) {
-                do_ecn = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv4.ecnpkts");
+            if(do_ecn == CONFIG_BOOLEAN_YES || (do_ecn == CONFIG_BOOLEAN_AUTO && (ipext_InCEPkts || ipext_InECT0Pkts || ipext_InECT1Pkts || ipext_InNoECTPkts))) {
+                do_ecn = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv4.ecnpkts");
                 if(unlikely(!st)) {
-                    st = rrdset_create("ipv4", "ecnpkts", NULL, "ecn", NULL, "IPv4 ECN Statistics", "packets/s", 8700, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
-
-                    rrddim_add(st, "InCEPkts", "CEP", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InNoECTPkts", "NoECTP", -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InECT0Pkts", "ECTP0", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InECT1Pkts", "ECTP1", 1, 1, RRDDIM_INCREMENTAL);
+                    st = rrdset_create_localhost("ipv4", "ecnpkts", NULL, "ecn", NULL, "IPv4 ECN Statistics"
+                                                 , "packets/s", 8700, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                    rrddim_add(st, "InCEPkts", "CEP", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InNoECTPkts", "NoECTP", -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InECT0Pkts", "ECTP0", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InECT1Pkts", "ECTP1", 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -379,13 +386,14 @@ int do_proc_net_netstat(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if(do_tcpext_memory == CONFIG_ONDEMAND_YES || (do_tcpext_memory == CONFIG_ONDEMAND_ONDEMAND && (tcpext_TCPMemoryPressures))) {
-                do_tcpext_memory = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv4.tcpmemorypressures");
+            if(do_tcpext_memory == CONFIG_BOOLEAN_YES || (do_tcpext_memory == CONFIG_BOOLEAN_AUTO && (tcpext_TCPMemoryPressures))) {
+                do_tcpext_memory = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv4.tcpmemorypressures");
                 if(unlikely(!st)) {
-                    st = rrdset_create("ipv4", "tcpmemorypressures", NULL, "tcp", NULL, "TCP Memory Pressures", "events/s", 3000, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv4", "tcpmemorypressures", NULL, "tcp", NULL, "TCP Memory Pressures"
+                                                 , "events/s", 3000, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "TCPMemoryPressures",   "pressures",  1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "TCPMemoryPressures",   "pressures",  1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -395,18 +403,19 @@ int do_proc_net_netstat(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if(do_tcpext_connaborts == CONFIG_ONDEMAND_YES || (do_tcpext_connaborts == CONFIG_ONDEMAND_ONDEMAND && (tcpext_TCPAbortOnData || tcpext_TCPAbortOnClose || tcpext_TCPAbortOnMemory || tcpext_TCPAbortOnTimeout || tcpext_TCPAbortOnLinger || tcpext_TCPAbortFailed))) {
-                do_tcpext_connaborts = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv4.tcpconnaborts");
+            if(do_tcpext_connaborts == CONFIG_BOOLEAN_YES || (do_tcpext_connaborts == CONFIG_BOOLEAN_AUTO && (tcpext_TCPAbortOnData || tcpext_TCPAbortOnClose || tcpext_TCPAbortOnMemory || tcpext_TCPAbortOnTimeout || tcpext_TCPAbortOnLinger || tcpext_TCPAbortFailed))) {
+                do_tcpext_connaborts = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv4.tcpconnaborts");
                 if(unlikely(!st)) {
-                    st = rrdset_create("ipv4", "tcpconnaborts", NULL, "tcp", NULL, "TCP Connection Aborts", "connections/s", 3010, update_every, RRDSET_TYPE_LINE);
-
-                    rrddim_add(st, "TCPAbortOnData",    "baddata",     1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "TCPAbortOnClose",   "userclosed",  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "TCPAbortOnMemory",  "nomemory",    1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "TCPAbortOnTimeout", "timeout",     1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "TCPAbortOnLinger",  "linger",      1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "TCPAbortFailed",    "failed",     -1, 1, RRDDIM_INCREMENTAL);
+                    st = rrdset_create_localhost("ipv4", "tcpconnaborts", NULL, "tcp", NULL, "TCP Connection Aborts"
+                                                 , "connections/s", 3010, update_every, RRDSET_TYPE_LINE);
+
+                    rrddim_add(st, "TCPAbortOnData",    "baddata",     1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "TCPAbortOnClose",   "userclosed",  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "TCPAbortOnMemory",  "nomemory",    1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "TCPAbortOnTimeout", "timeout",     1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "TCPAbortOnLinger",  "linger",      1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "TCPAbortFailed",    "failed",     -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -420,16 +429,18 @@ int do_proc_net_netstat(int update_every, usec_t dt) {
             }
             // --------------------------------------------------------------------
 
-            if(do_tcpext_reorder == CONFIG_ONDEMAND_YES || (do_tcpext_reorder == CONFIG_ONDEMAND_ONDEMAND && (tcpext_TCPRenoReorder || tcpext_TCPFACKReorder || tcpext_TCPSACKReorder || tcpext_TCPTSReorder))) {
-                do_tcpext_reorder = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv4.tcpreorders");
+            if(do_tcpext_reorder == CONFIG_BOOLEAN_YES || (do_tcpext_reorder == CONFIG_BOOLEAN_AUTO && (tcpext_TCPRenoReorder || tcpext_TCPFACKReorder || tcpext_TCPSACKReorder || tcpext_TCPTSReorder))) {
+                do_tcpext_reorder = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv4.tcpreorders");
                 if(unlikely(!st)) {
-                    st = rrdset_create("ipv4", "tcpreorders", NULL, "tcp", NULL, "TCP Reordered Packets by Detection Method", "packets/s", 3020, update_every, RRDSET_TYPE_LINE);
-
-                    rrddim_add(st, "TCPTSReorder",   "timestamp",   1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "TCPSACKReorder", "sack",        1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "TCPFACKReorder", "fack",        1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "TCPRenoReorder", "reno",        1, 1, RRDDIM_INCREMENTAL);
+                    st = rrdset_create_localhost("ipv4", "tcpreorders", NULL, "tcp", NULL
+                                                 , "TCP Reordered Packets by Detection Method", "packets/s", 3020
+                                                 , update_every, RRDSET_TYPE_LINE);
+
+                    rrddim_add(st, "TCPTSReorder",   "timestamp",   1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "TCPSACKReorder", "sack",        1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "TCPFACKReorder", "fack",        1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "TCPRenoReorder", "reno",        1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -442,16 +453,17 @@ int do_proc_net_netstat(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if(do_tcpext_ofo == CONFIG_ONDEMAND_YES || (do_tcpext_ofo == CONFIG_ONDEMAND_ONDEMAND && (tcpext_TCPOFOQueue || tcpext_TCPOFODrop || tcpext_TCPOFOMerge))) {
-                do_tcpext_ofo = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv4.tcpofo");
+            if(do_tcpext_ofo == CONFIG_BOOLEAN_YES || (do_tcpext_ofo == CONFIG_BOOLEAN_AUTO && (tcpext_TCPOFOQueue || tcpext_TCPOFODrop || tcpext_TCPOFOMerge))) {
+                do_tcpext_ofo = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv4.tcpofo");
                 if(unlikely(!st)) {
-                    st = rrdset_create("ipv4", "tcpofo", NULL, "tcp", NULL, "TCP Out-Of-Order Queue", "packets/s", 3050, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv4", "tcpofo", NULL, "tcp", NULL, "TCP Out-Of-Order Queue"
+                                                 , "packets/s", 3050, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "TCPOFOQueue", "inqueue",  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "TCPOFODrop",  "dropped", -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "TCPOFOMerge", "merged",   1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OfoPruned",   "pruned",  -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "TCPOFOQueue", "inqueue",  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "TCPOFODrop",  "dropped", -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "TCPOFOMerge", "merged",   1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OfoPruned",   "pruned",  -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -464,15 +476,16 @@ int do_proc_net_netstat(int update_every, usec_t dt) {
 
             // --------------------------------------------------------------------
 
-            if(do_tcpext_syscookies == CONFIG_ONDEMAND_YES || (do_tcpext_syscookies == CONFIG_ONDEMAND_ONDEMAND && (tcpext_SyncookiesSent || tcpext_SyncookiesRecv || tcpext_SyncookiesFailed))) {
-                do_tcpext_syscookies = CONFIG_ONDEMAND_YES;
-                st = rrdset_find("ipv4.tcpsyncookies");
+            if(do_tcpext_syscookies == CONFIG_BOOLEAN_YES || (do_tcpext_syscookies == CONFIG_BOOLEAN_AUTO && (tcpext_SyncookiesSent || tcpext_SyncookiesRecv || tcpext_SyncookiesFailed))) {
+                do_tcpext_syscookies = CONFIG_BOOLEAN_YES;
+                st = rrdset_find_localhost("ipv4.tcpsyncookies");
                 if(unlikely(!st)) {
-                    st = rrdset_create("ipv4", "tcpsyncookies", NULL, "tcp", NULL, "TCP SYN Cookies", "packets/s", 3100, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("ipv4", "tcpsyncookies", NULL, "tcp", NULL, "TCP SYN Cookies"
+                                                 , "packets/s", 3100, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "SyncookiesRecv",   "received",  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "SyncookiesSent",   "sent",     -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "SyncookiesFailed", "failed",   -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "SyncookiesRecv",   "received",  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "SyncookiesSent",   "sent",     -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "SyncookiesFailed", "failed",   -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
index e7f6bb6ea727ca41508e8831bb4dcbd8ba56f427..0df91963560d1b0a91c96801e92c21ffe19953e4 100644 (file)
@@ -269,13 +269,14 @@ int do_proc_net_rpc_nfs(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(do_net == 2) {
-        st = rrdset_find_bytype("nfs", "net");
+        st = rrdset_find_bytype_localhost("nfs", "net");
         if(!st) {
-            st = rrdset_create("nfs", "net", NULL, "network", NULL, "NFS Client Network", "operations/s", 5007, update_every, RRDSET_TYPE_STACKED);
-            st->isdetail = 1;
+            st = rrdset_create_localhost("nfs", "net", NULL, "network", NULL, "NFS Client Network", "operations/s", 5007
+                                         , update_every, RRDSET_TYPE_STACKED);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-            rrddim_add(st, "udp", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "tcp", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "udp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "tcp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -291,14 +292,15 @@ int do_proc_net_rpc_nfs(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(do_rpc == 2) {
-        st = rrdset_find_bytype("nfs", "rpc");
+        st = rrdset_find_bytype_localhost("nfs", "rpc");
         if(!st) {
-            st = rrdset_create("nfs", "rpc", NULL, "rpc", NULL, "NFS Client Remote Procedure Calls Statistics", "calls/s", 5008, update_every, RRDSET_TYPE_LINE);
-            st->isdetail = 1;
+            st = rrdset_create_localhost("nfs", "rpc", NULL, "rpc", NULL, "NFS Client Remote Procedure Calls Statistics"
+                                         , "calls/s", 5008, update_every, RRDSET_TYPE_LINE);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-            rrddim_add(st, "calls", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "retransmits", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "auth_refresh", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "calls", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "retransmits", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "auth_refresh", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -312,12 +314,13 @@ int do_proc_net_rpc_nfs(int update_every, usec_t dt) {
 
     if(do_proc2 == 2) {
         unsigned int i;
-        st = rrdset_find_bytype("nfs", "proc2");
+        st = rrdset_find_bytype_localhost("nfs", "proc2");
         if(!st) {
-            st = rrdset_create("nfs", "proc2", NULL, "nfsv2rpc", NULL, "NFS v2 Client Remote Procedure Calls", "calls/s", 5009, update_every, RRDSET_TYPE_STACKED);
+            st = rrdset_create_localhost("nfs", "proc2", NULL, "nfsv2rpc", NULL, "NFS v2 Client Remote Procedure Calls"
+                                         , "calls/s", 5009, update_every, RRDSET_TYPE_STACKED);
 
             for(i = 0; nfs_proc2_values[i].present ; i++)
-                rrddim_add(st, nfs_proc2_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, nfs_proc2_values[i].name, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -331,12 +334,13 @@ int do_proc_net_rpc_nfs(int update_every, usec_t dt) {
 
     if(do_proc3 == 2) {
         unsigned int i;
-        st = rrdset_find_bytype("nfs", "proc3");
+        st = rrdset_find_bytype_localhost("nfs", "proc3");
         if(!st) {
-            st = rrdset_create("nfs", "proc3", NULL, "nfsv3rpc", NULL, "NFS v3 Client Remote Procedure Calls", "calls/s", 5010, update_every, RRDSET_TYPE_STACKED);
+            st = rrdset_create_localhost("nfs", "proc3", NULL, "nfsv3rpc", NULL, "NFS v3 Client Remote Procedure Calls"
+                                         , "calls/s", 5010, update_every, RRDSET_TYPE_STACKED);
 
             for(i = 0; nfs_proc3_values[i].present ; i++)
-                rrddim_add(st, nfs_proc3_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, nfs_proc3_values[i].name, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -350,12 +354,13 @@ int do_proc_net_rpc_nfs(int update_every, usec_t dt) {
 
     if(do_proc4 == 2) {
         unsigned int i;
-        st = rrdset_find_bytype("nfs", "proc4");
+        st = rrdset_find_bytype_localhost("nfs", "proc4");
         if(!st) {
-            st = rrdset_create("nfs", "proc4", NULL, "nfsv4rpc", NULL, "NFS v4 Client Remote Procedure Calls", "calls/s", 5011, update_every, RRDSET_TYPE_STACKED);
+            st = rrdset_create_localhost("nfs", "proc4", NULL, "nfsv4rpc", NULL, "NFS v4 Client Remote Procedure Calls"
+                                         , "calls/s", 5011, update_every, RRDSET_TYPE_STACKED);
 
             for(i = 0; nfs_proc4_values[i].present ; i++)
-                rrddim_add(st, nfs_proc4_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, nfs_proc4_values[i].name, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
index 969910f32c85b9ed46eebc0cd851fda546b3b7ea..b0ed58d13615ed548b1d225af1dc6436e035e18f 100644 (file)
@@ -492,13 +492,14 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(do_rc == 2) {
-        st = rrdset_find_bytype("nfsd", "readcache");
+        st = rrdset_find_bytype_localhost("nfsd", "readcache");
         if(!st) {
-            st = rrdset_create("nfsd", "readcache", NULL, "cache", NULL, "NFS Server Read Cache", "reads/s", 5000, update_every, RRDSET_TYPE_STACKED);
+            st = rrdset_create_localhost("nfsd", "readcache", NULL, "cache", NULL, "NFS Server Read Cache", "reads/s"
+                                         , 5000, update_every, RRDSET_TYPE_STACKED);
 
-            rrddim_add(st, "hits", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "misses", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "nocache", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "hits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "misses", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "nocache", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -511,16 +512,17 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(do_fh == 2) {
-        st = rrdset_find_bytype("nfsd", "filehandles");
+        st = rrdset_find_bytype_localhost("nfsd", "filehandles");
         if(!st) {
-            st = rrdset_create("nfsd", "filehandles", NULL, "filehandles", NULL, "NFS Server File Handles", "handles/s", 5001, update_every, RRDSET_TYPE_LINE);
-            st->isdetail = 1;
-
-            rrddim_add(st, "stale", NULL, 1, 1, RRDDIM_ABSOLUTE);
-            rrddim_add(st, "total_lookups", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "anonymous_lookups", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "dir_not_in_dcache", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "non_dir_not_in_dcache", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            st = rrdset_create_localhost("nfsd", "filehandles", NULL, "filehandles", NULL, "NFS Server File Handles"
+                                         , "handles/s", 5001, update_every, RRDSET_TYPE_LINE);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+            rrddim_add(st, "stale", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+            rrddim_add(st, "total_lookups", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "anonymous_lookups", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "dir_not_in_dcache", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "non_dir_not_in_dcache", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -535,12 +537,13 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(do_io == 2) {
-        st = rrdset_find_bytype("nfsd", "io");
+        st = rrdset_find_bytype_localhost("nfsd", "io");
         if(!st) {
-            st = rrdset_create("nfsd", "io", NULL, "io", NULL, "NFS Server I/O", "kilobytes/s", 5002, update_every, RRDSET_TYPE_AREA);
+            st = rrdset_create_localhost("nfsd", "io", NULL, "io", NULL, "NFS Server I/O", "kilobytes/s", 5002
+                                         , update_every, RRDSET_TYPE_AREA);
 
-            rrddim_add(st, "read", NULL, 1, 1000, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "write", NULL, -1, 1000, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "read", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "write", NULL, -1, 1000, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -552,42 +555,47 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(do_th == 2) {
-        st = rrdset_find_bytype("nfsd", "threads");
+        st = rrdset_find_bytype_localhost("nfsd", "threads");
         if(!st) {
-            st = rrdset_create("nfsd", "threads", NULL, "threads", NULL, "NFS Server Threads", "threads", 5003, update_every, RRDSET_TYPE_LINE);
+            st = rrdset_create_localhost("nfsd", "threads", NULL, "threads", NULL, "NFS Server Threads", "threads", 5003
+                                         , update_every, RRDSET_TYPE_LINE);
 
-            rrddim_add(st, "threads", NULL, 1, 1, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "threads", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
         }
         else rrdset_next(st);
 
         rrddim_set(st, "threads", th_threads);
         rrdset_done(st);
 
-        st = rrdset_find_bytype("nfsd", "threads_fullcnt");
+        st = rrdset_find_bytype_localhost("nfsd", "threads_fullcnt");
         if(!st) {
-            st = rrdset_create("nfsd", "threads_fullcnt", NULL, "threads", NULL, "NFS Server Threads Full Count", "ops/s", 5004, update_every, RRDSET_TYPE_LINE);
+            st = rrdset_create_localhost("nfsd", "threads_fullcnt", NULL, "threads", NULL
+                                         , "NFS Server Threads Full Count", "ops/s", 5004, update_every
+                                         , RRDSET_TYPE_LINE);
 
-            rrddim_add(st, "full_count", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "full_count", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
         rrddim_set(st, "full_count", th_fullcnt);
         rrdset_done(st);
 
-        st = rrdset_find_bytype("nfsd", "threads_histogram");
+        st = rrdset_find_bytype_localhost("nfsd", "threads_histogram");
         if(!st) {
-            st = rrdset_create("nfsd", "threads_histogram", NULL, "threads", NULL, "NFS Server Threads Usage Histogram", "percentage", 5005, update_every, RRDSET_TYPE_LINE);
-
-            rrddim_add(st, "0%-10%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-            rrddim_add(st, "10%-20%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-            rrddim_add(st, "20%-30%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-            rrddim_add(st, "30%-40%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-            rrddim_add(st, "40%-50%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-            rrddim_add(st, "50%-60%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-            rrddim_add(st, "60%-70%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-            rrddim_add(st, "70%-80%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-            rrddim_add(st, "80%-90%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-            rrddim_add(st, "90%-100%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+            st = rrdset_create_localhost("nfsd", "threads_histogram", NULL, "threads", NULL
+                                         , "NFS Server Threads Usage Histogram", "percentage", 5005, update_every
+                                         , RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "0%-10%", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
+            rrddim_add(st, "10%-20%", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
+            rrddim_add(st, "20%-30%", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
+            rrddim_add(st, "30%-40%", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
+            rrddim_add(st, "40%-50%", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
+            rrddim_add(st, "50%-60%", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
+            rrddim_add(st, "60%-70%", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
+            rrddim_add(st, "70%-80%", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
+            rrddim_add(st, "80%-90%", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
+            rrddim_add(st, "90%-100%", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
         }
         else rrdset_next(st);
 
@@ -607,21 +615,22 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(do_ra == 2) {
-        st = rrdset_find_bytype("nfsd", "readahead");
+        st = rrdset_find_bytype_localhost("nfsd", "readahead");
         if(!st) {
-            st = rrdset_create("nfsd", "readahead", NULL, "readahead", NULL, "NFS Server Read Ahead Depth", "percentage", 5005, update_every, RRDSET_TYPE_STACKED);
-
-            rrddim_add(st, "10%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-            rrddim_add(st, "20%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-            rrddim_add(st, "30%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-            rrddim_add(st, "40%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-            rrddim_add(st, "50%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-            rrddim_add(st, "60%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-            rrddim_add(st, "70%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-            rrddim_add(st, "80%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-            rrddim_add(st, "90%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-            rrddim_add(st, "100%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-            rrddim_add(st, "misses", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+            st = rrdset_create_localhost("nfsd", "readahead", NULL, "readahead", NULL, "NFS Server Read Ahead Depth"
+                                         , "percentage", 5005, update_every, RRDSET_TYPE_STACKED);
+
+            rrddim_add(st, "10%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+            rrddim_add(st, "20%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+            rrddim_add(st, "30%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+            rrddim_add(st, "40%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+            rrddim_add(st, "50%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+            rrddim_add(st, "60%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+            rrddim_add(st, "70%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+            rrddim_add(st, "80%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+            rrddim_add(st, "90%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+            rrddim_add(st, "100%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+            rrddim_add(st, "misses", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
         }
         else rrdset_next(st);
 
@@ -645,13 +654,14 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(do_net == 2) {
-        st = rrdset_find_bytype("nfsd", "net");
+        st = rrdset_find_bytype_localhost("nfsd", "net");
         if(!st) {
-            st = rrdset_create("nfsd", "net", NULL, "network", NULL, "NFS Server Network Statistics", "packets/s", 5007, update_every, RRDSET_TYPE_STACKED);
-            st->isdetail = 1;
+            st = rrdset_create_localhost("nfsd", "net", NULL, "network", NULL, "NFS Server Network Statistics"
+                                         , "packets/s", 5007, update_every, RRDSET_TYPE_STACKED);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-            rrddim_add(st, "udp", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "tcp", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "udp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "tcp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -667,14 +677,16 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(do_rpc == 2) {
-        st = rrdset_find_bytype("nfsd", "rpc");
+        st = rrdset_find_bytype_localhost("nfsd", "rpc");
         if(!st) {
-            st = rrdset_create("nfsd", "rpc", NULL, "rpc", NULL, "NFS Server Remote Procedure Calls Statistics", "calls/s", 5008, update_every, RRDSET_TYPE_LINE);
-            st->isdetail = 1;
-
-            rrddim_add(st, "calls", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "bad_format", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "bad_auth", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            st = rrdset_create_localhost("nfsd", "rpc", NULL, "rpc", NULL
+                                         , "NFS Server Remote Procedure Calls Statistics", "calls/s", 5008, update_every
+                                         , RRDSET_TYPE_LINE);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+            rrddim_add(st, "calls", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "bad_format", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "bad_auth", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -691,12 +703,13 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) {
 
     if(do_proc2 == 2) {
         unsigned int i;
-        st = rrdset_find_bytype("nfsd", "proc2");
+        st = rrdset_find_bytype_localhost("nfsd", "proc2");
         if(!st) {
-            st = rrdset_create("nfsd", "proc2", NULL, "nfsv2rpc", NULL, "NFS v2 Server Remote Procedure Calls", "calls/s", 5009, update_every, RRDSET_TYPE_STACKED);
+            st = rrdset_create_localhost("nfsd", "proc2", NULL, "nfsv2rpc", NULL, "NFS v2 Server Remote Procedure Calls"
+                                         , "calls/s", 5009, update_every, RRDSET_TYPE_STACKED);
 
             for(i = 0; nfsd_proc2_values[i].present ; i++)
-                rrddim_add(st, nfsd_proc2_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, nfsd_proc2_values[i].name, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -710,12 +723,13 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) {
 
     if(do_proc3 == 2) {
         unsigned int i;
-        st = rrdset_find_bytype("nfsd", "proc3");
+        st = rrdset_find_bytype_localhost("nfsd", "proc3");
         if(!st) {
-            st = rrdset_create("nfsd", "proc3", NULL, "nfsv3rpc", NULL, "NFS v3 Server Remote Procedure Calls", "calls/s", 5010, update_every, RRDSET_TYPE_STACKED);
+            st = rrdset_create_localhost("nfsd", "proc3", NULL, "nfsv3rpc", NULL, "NFS v3 Server Remote Procedure Calls"
+                                         , "calls/s", 5010, update_every, RRDSET_TYPE_STACKED);
 
             for(i = 0; nfsd_proc3_values[i].present ; i++)
-                rrddim_add(st, nfsd_proc3_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, nfsd_proc3_values[i].name, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -729,12 +743,13 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) {
 
     if(do_proc4 == 2) {
         unsigned int i;
-        st = rrdset_find_bytype("nfsd", "proc4");
+        st = rrdset_find_bytype_localhost("nfsd", "proc4");
         if(!st) {
-            st = rrdset_create("nfsd", "proc4", NULL, "nfsv4rpc", NULL, "NFS v4 Server Remote Procedure Calls", "calls/s", 5011, update_every, RRDSET_TYPE_STACKED);
+            st = rrdset_create_localhost("nfsd", "proc4", NULL, "nfsv4rpc", NULL, "NFS v4 Server Remote Procedure Calls"
+                                         , "calls/s", 5011, update_every, RRDSET_TYPE_STACKED);
 
             for(i = 0; nfsd_proc4_values[i].present ; i++)
-                rrddim_add(st, nfsd_proc4_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, nfsd_proc4_values[i].name, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -748,12 +763,13 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) {
 
     if(do_proc4ops == 2) {
         unsigned int i;
-        st = rrdset_find_bytype("nfsd", "proc4ops");
+        st = rrdset_find_bytype_localhost("nfsd", "proc4ops");
         if(!st) {
-            st = rrdset_create("nfsd", "proc4ops", NULL, "nfsv2ops", NULL, "NFS v4 Server Operations", "operations/s", 5012, update_every, RRDSET_TYPE_STACKED);
+            st = rrdset_create_localhost("nfsd", "proc4ops", NULL, "nfsv2ops", NULL, "NFS v4 Server Operations"
+                                         , "operations/s", 5012, update_every, RRDSET_TYPE_STACKED);
 
             for(i = 0; nfsd4_ops_values[i].present ; i++)
-                rrddim_add(st, nfsd4_ops_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, nfsd4_ops_values[i].name, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
index 7707d3b789a76903a4a859dc5815b069c80e430e..ba7b4001314983a25c2ba53ecdb9ff918fcedb38 100644 (file)
@@ -392,14 +392,15 @@ int do_proc_net_snmp(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if(do_ip_packets) {
-                st = rrdset_find(RRD_TYPE_NET_SNMP ".packets");
+                st = rrdset_find_localhost(RRD_TYPE_NET_SNMP ".packets");
                 if(!st) {
-                    st = rrdset_create(RRD_TYPE_NET_SNMP, "packets", NULL, "packets", NULL, "IPv4 Packets", "packets/s", 3000, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "packets", NULL, "packets", NULL, "IPv4 Packets"
+                                                 , "packets/s", 3000, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InReceives",    "received",  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutRequests",   "sent",     -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "ForwDatagrams", "forwarded", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InDelivers",    "delivered", 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InReceives",    "received",  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutRequests",   "sent",     -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "ForwDatagrams", "forwarded", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InDelivers",    "delivered", 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -413,14 +414,16 @@ int do_proc_net_snmp(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if(do_ip_fragsout) {
-                st = rrdset_find(RRD_TYPE_NET_SNMP ".fragsout");
+                st = rrdset_find_localhost(RRD_TYPE_NET_SNMP ".fragsout");
                 if(!st) {
-                    st = rrdset_create(RRD_TYPE_NET_SNMP, "fragsout", NULL, "fragments", NULL, "IPv4 Fragments Sent", "packets/s", 3010, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
-
-                    rrddim_add(st, "FragOKs",     "ok",      1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "FragFails",   "failed", -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "FragCreates", "created", 1, 1, RRDDIM_INCREMENTAL);
+                    st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "fragsout", NULL, "fragments", NULL
+                                                 , "IPv4 Fragments Sent", "packets/s", 3010, update_every
+                                                 , RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                    rrddim_add(st, "FragOKs",     "ok",      1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "FragFails",   "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "FragCreates", "created", 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -433,14 +436,16 @@ int do_proc_net_snmp(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if(do_ip_fragsin) {
-                st = rrdset_find(RRD_TYPE_NET_SNMP ".fragsin");
+                st = rrdset_find_localhost(RRD_TYPE_NET_SNMP ".fragsin");
                 if(!st) {
-                    st = rrdset_create(RRD_TYPE_NET_SNMP, "fragsin", NULL, "fragments", NULL, "IPv4 Fragments Reassembly", "packets/s", 3011, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
-
-                    rrddim_add(st, "ReasmOKs",   "ok",      1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "ReasmFails", "failed", -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "ReasmReqds", "all",     1, 1, RRDDIM_INCREMENTAL);
+                    st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "fragsin", NULL, "fragments", NULL
+                                                 , "IPv4 Fragments Reassembly", "packets/s", 3011, update_every
+                                                 , RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                    rrddim_add(st, "ReasmOKs",   "ok",      1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "ReasmFails", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "ReasmReqds", "all",     1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -453,19 +458,20 @@ int do_proc_net_snmp(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if(do_ip_errors) {
-                st = rrdset_find(RRD_TYPE_NET_SNMP ".errors");
+                st = rrdset_find_localhost(RRD_TYPE_NET_SNMP ".errors");
                 if(!st) {
-                    st = rrdset_create(RRD_TYPE_NET_SNMP, "errors", NULL, "errors", NULL, "IPv4 Errors", "packets/s", 3002, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "errors", NULL, "errors", NULL, "IPv4 Errors"
+                                                 , "packets/s", 3002, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "InDiscards",      NULL,  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutDiscards",     NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InDiscards",      NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutDiscards",     NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
 
-                    rrddim_add(st, "InHdrErrors",     NULL,  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutNoRoutes",     NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InHdrErrors",     NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutNoRoutes",     NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
 
-                    rrddim_add(st, "InAddrErrors",    NULL,  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InUnknownProtos", NULL,  1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InAddrErrors",    NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InUnknownProtos", NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -497,12 +503,13 @@ int do_proc_net_snmp(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if(do_icmp_packets) {
-                st = rrdset_find(RRD_TYPE_NET_SNMP ".icmp");
+                st = rrdset_find_localhost(RRD_TYPE_NET_SNMP ".icmp");
                 if(!st) {
-                    st = rrdset_create(RRD_TYPE_NET_SNMP, "icmp", NULL, "icmp", NULL, "IPv4 ICMP Packets", "packets/s", 2602, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "icmp", NULL, "icmp", NULL, "IPv4 ICMP Packets"
+                                                 , "packets/s", 2602, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InMsgs",  "received",  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutMsgs", "sent",     -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InMsgs",  "received",  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutMsgs", "sent",     -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -511,13 +518,15 @@ int do_proc_net_snmp(int update_every, usec_t dt) {
 
                 rrdset_done(st);
 
-                st = rrdset_find(RRD_TYPE_NET_SNMP ".icmp_errors");
+                st = rrdset_find_localhost(RRD_TYPE_NET_SNMP ".icmp_errors");
                 if(!st) {
-                    st = rrdset_create(RRD_TYPE_NET_SNMP, "icmp_errors", NULL, "icmp", NULL, "IPv4 ICMP Errors", "packets/s", 2603, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "icmp_errors", NULL, "icmp", NULL
+                                                 , "IPv4 ICMP Errors", "packets/s", 2603, update_every
+                                                 , RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InErrors",     NULL,  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutErrors",    NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InCsumErrors", NULL,  1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InErrors",     NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutErrors",    NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InCsumErrors", NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -543,12 +552,13 @@ int do_proc_net_snmp(int update_every, usec_t dt) {
             if(do_icmpmsg) {
                 int i;
 
-                st = rrdset_find(RRD_TYPE_NET_SNMP ".icmpmsg");
+                st = rrdset_find_localhost(RRD_TYPE_NET_SNMP ".icmpmsg");
                 if(!st) {
-                    st = rrdset_create(RRD_TYPE_NET_SNMP, "icmpmsg", NULL, "icmp", NULL, "IPv4 ICMP Messsages", "packets/s", 2604, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "icmpmsg", NULL, "icmp", NULL, "IPv4 ICMP Messsages"
+                                                 , "packets/s", 2604, update_every, RRDSET_TYPE_LINE);
 
                     for(i = 0; icmpmsg_data[i].name ;i++)
-                        rrddim_add(st, icmpmsg_data[i].name, icmpmsg_data[i].label,  icmpmsg_data[i].multiplier, 1, RRDDIM_INCREMENTAL);
+                        rrddim_add(st, icmpmsg_data[i].name, icmpmsg_data[i].label,  icmpmsg_data[i].multiplier, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -578,11 +588,12 @@ int do_proc_net_snmp(int update_every, usec_t dt) {
 
             // see http://net-snmp.sourceforge.net/docs/mibs/tcp.html
             if(do_tcp_sockets) {
-                st = rrdset_find(RRD_TYPE_NET_SNMP ".tcpsock");
+                st = rrdset_find_localhost(RRD_TYPE_NET_SNMP ".tcpsock");
                 if(!st) {
-                    st = rrdset_create(RRD_TYPE_NET_SNMP, "tcpsock", NULL, "tcp", NULL, "IPv4 TCP Connections", "active connections", 2500, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "tcpsock", NULL, "tcp", NULL, "IPv4 TCP Connections"
+                                                 , "active connections", 2500, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "CurrEstab", "connections", 1, 1, RRDDIM_ABSOLUTE);
+                    rrddim_add(st, "CurrEstab", "connections", 1, 1, RRD_ALGORITHM_ABSOLUTE);
                 }
                 else rrdset_next(st);
 
@@ -593,12 +604,13 @@ int do_proc_net_snmp(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if(do_tcp_packets) {
-                st = rrdset_find(RRD_TYPE_NET_SNMP ".tcppackets");
+                st = rrdset_find_localhost(RRD_TYPE_NET_SNMP ".tcppackets");
                 if(!st) {
-                    st = rrdset_create(RRD_TYPE_NET_SNMP, "tcppackets", NULL, "tcp", NULL, "IPv4 TCP Packets", "packets/s", 2600, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "tcppackets", NULL, "tcp", NULL, "IPv4 TCP Packets"
+                                                 , "packets/s", 2600, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InSegs",  "received", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutSegs", "sent",    -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InSegs",  "received", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutSegs", "sent",    -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -610,14 +622,15 @@ int do_proc_net_snmp(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if(do_tcp_errors) {
-                st = rrdset_find(RRD_TYPE_NET_SNMP ".tcperrors");
+                st = rrdset_find_localhost(RRD_TYPE_NET_SNMP ".tcperrors");
                 if(!st) {
-                    st = rrdset_create(RRD_TYPE_NET_SNMP, "tcperrors", NULL, "tcp", NULL, "IPv4 TCP Errors", "packets/s", 2700, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "tcperrors", NULL, "tcp", NULL, "IPv4 TCP Errors"
+                                                 , "packets/s", 2700, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "InErrs",       NULL,  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InCsumErrors", NULL,  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "RetransSegs",  NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InErrs",       NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InCsumErrors", NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "RetransSegs",  NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -630,16 +643,18 @@ int do_proc_net_snmp(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if(do_tcp_handshake) {
-                st = rrdset_find(RRD_TYPE_NET_SNMP ".tcphandshake");
+                st = rrdset_find_localhost(RRD_TYPE_NET_SNMP ".tcphandshake");
                 if(!st) {
-                    st = rrdset_create(RRD_TYPE_NET_SNMP, "tcphandshake", NULL, "tcp", NULL, "IPv4 TCP Handshake Issues", "events/s", 2900, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
-
-                    rrddim_add(st, "EstabResets",  NULL,  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutRsts",      NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "ActiveOpens",  NULL,  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "PassiveOpens", NULL,  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "AttemptFails", NULL,  1, 1, RRDDIM_INCREMENTAL);
+                    st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "tcphandshake", NULL, "tcp", NULL
+                                                 , "IPv4 TCP Handshake Issues", "events/s", 2900, update_every
+                                                 , RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                    rrddim_add(st, "EstabResets",  NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutRsts",      NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "ActiveOpens",  NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "PassiveOpens", NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "AttemptFails", NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -671,12 +686,13 @@ int do_proc_net_snmp(int update_every, usec_t dt) {
 
             // see http://net-snmp.sourceforge.net/docs/mibs/udp.html
             if(do_udp_packets) {
-                st = rrdset_find(RRD_TYPE_NET_SNMP ".udppackets");
+                st = rrdset_find_localhost(RRD_TYPE_NET_SNMP ".udppackets");
                 if(!st) {
-                    st = rrdset_create(RRD_TYPE_NET_SNMP, "udppackets", NULL, "udp", NULL, "IPv4 UDP Packets", "packets/s", 2601, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "udppackets", NULL, "udp", NULL, "IPv4 UDP Packets"
+                                                 , "packets/s", 2601, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InDatagrams",  "received", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutDatagrams", "sent",    -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InDatagrams",  "received", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutDatagrams", "sent",    -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -688,17 +704,18 @@ int do_proc_net_snmp(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if(do_udp_errors) {
-                st = rrdset_find(RRD_TYPE_NET_SNMP ".udperrors");
+                st = rrdset_find_localhost(RRD_TYPE_NET_SNMP ".udperrors");
                 if(!st) {
-                    st = rrdset_create(RRD_TYPE_NET_SNMP, "udperrors", NULL, "udp", NULL, "IPv4 UDP Errors", "events/s", 2701, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
-
-                    rrddim_add(st, "RcvbufErrors", NULL,  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InErrors",     NULL,  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "NoPorts",      NULL,  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InCsumErrors", NULL,  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "IgnoredMulti", NULL,  1, 1, RRDDIM_INCREMENTAL);
+                    st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "udperrors", NULL, "udp", NULL, "IPv4 UDP Errors"
+                                                 , "events/s", 2701, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                    rrddim_add(st, "RcvbufErrors", NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InErrors",     NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "NoPorts",      NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InCsumErrors", NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "IgnoredMulti", NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -730,12 +747,14 @@ int do_proc_net_snmp(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if(do_udplite_packets) {
-                st = rrdset_find(RRD_TYPE_NET_SNMP ".udplite");
+                st = rrdset_find_localhost(RRD_TYPE_NET_SNMP ".udplite");
                 if(!st) {
-                    st = rrdset_create(RRD_TYPE_NET_SNMP, "udplite", NULL, "udplite", NULL, "IPv4 UDPLite Packets", "packets/s", 2603, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "udplite", NULL, "udplite", NULL
+                                                 , "IPv4 UDPLite Packets", "packets/s", 2603, update_every
+                                                 , RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "InDatagrams",  "received", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "OutDatagrams", "sent",    -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InDatagrams",  "received", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "OutDatagrams", "sent",    -1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -743,16 +762,18 @@ int do_proc_net_snmp(int update_every, usec_t dt) {
                 rrddim_set(st, "OutDatagrams", *udplite_OutDatagrams);
                 rrdset_done(st);
 
-                st = rrdset_find(RRD_TYPE_NET_SNMP ".udplite_errors");
+                st = rrdset_find_localhost(RRD_TYPE_NET_SNMP ".udplite_errors");
                 if(!st) {
-                    st = rrdset_create(RRD_TYPE_NET_SNMP, "udplite_errors", NULL, "udplite", NULL, "IPv4 UDPLite Errors", "packets/s", 2604, update_every, RRDSET_TYPE_LINE);
-
-                    rrddim_add(st, "RcvbufErrors", NULL,  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "NoPorts",      NULL,  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "IgnoredMulti", NULL,  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InErrors",     NULL,  1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "InCsumErrors", NULL,  1, 1, RRDDIM_INCREMENTAL);
+                    st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "udplite_errors", NULL, "udplite", NULL
+                                                 , "IPv4 UDPLite Errors", "packets/s", 2604, update_every
+                                                 , RRDSET_TYPE_LINE);
+
+                    rrddim_add(st, "RcvbufErrors", NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "NoPorts",      NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "IgnoredMulti", NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InErrors",     NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "InCsumErrors", NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
index 278592153d968e49c6b95f9acc8c8a56adbf32f9..8c4581c1b57aa345287b720d2feb34d9a8ce4c48 100644 (file)
@@ -126,28 +126,28 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
     static unsigned long long UdpLite6InCsumErrors = 0ULL;
 
     if(unlikely(!arl_base)) {
-        do_ip_packets       = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 packets", CONFIG_ONDEMAND_ONDEMAND);
-        do_ip_fragsout      = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments sent", CONFIG_ONDEMAND_ONDEMAND);
-        do_ip_fragsin       = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments assembly", CONFIG_ONDEMAND_ONDEMAND);
-        do_ip_errors        = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 errors", CONFIG_ONDEMAND_ONDEMAND);
-        do_udp_packets      = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP packets", CONFIG_ONDEMAND_ONDEMAND);
-        do_udp_errors       = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP errors", CONFIG_ONDEMAND_ONDEMAND);
-        do_udplite_packets  = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite packets", CONFIG_ONDEMAND_ONDEMAND);
-        do_udplite_errors   = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite errors", CONFIG_ONDEMAND_ONDEMAND);
-        do_bandwidth        = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "bandwidth", CONFIG_ONDEMAND_ONDEMAND);
-        do_mcast            = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
-        do_bcast            = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "broadcast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
-        do_mcast_p          = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast packets", CONFIG_ONDEMAND_ONDEMAND);
-        do_icmp             = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp", CONFIG_ONDEMAND_ONDEMAND);
-        do_icmp_redir       = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp redirects", CONFIG_ONDEMAND_ONDEMAND);
-        do_icmp_errors      = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp errors", CONFIG_ONDEMAND_ONDEMAND);
-        do_icmp_echos       = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp echos", CONFIG_ONDEMAND_ONDEMAND);
-        do_icmp_groupmemb   = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp group membership", CONFIG_ONDEMAND_ONDEMAND);
-        do_icmp_router      = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp router", CONFIG_ONDEMAND_ONDEMAND);
-        do_icmp_neighbor    = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp neighbor", CONFIG_ONDEMAND_ONDEMAND);
-        do_icmp_mldv2       = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp mldv2", CONFIG_ONDEMAND_ONDEMAND);
-        do_icmp_types       = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp types", CONFIG_ONDEMAND_ONDEMAND);
-        do_ect              = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ect", CONFIG_ONDEMAND_ONDEMAND);
+        do_ip_packets       = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 packets", CONFIG_BOOLEAN_AUTO);
+        do_ip_fragsout      = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments sent", CONFIG_BOOLEAN_AUTO);
+        do_ip_fragsin       = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments assembly", CONFIG_BOOLEAN_AUTO);
+        do_ip_errors        = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 errors", CONFIG_BOOLEAN_AUTO);
+        do_udp_packets      = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP packets", CONFIG_BOOLEAN_AUTO);
+        do_udp_errors       = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP errors", CONFIG_BOOLEAN_AUTO);
+        do_udplite_packets  = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite packets", CONFIG_BOOLEAN_AUTO);
+        do_udplite_errors   = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite errors", CONFIG_BOOLEAN_AUTO);
+        do_bandwidth        = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "bandwidth", CONFIG_BOOLEAN_AUTO);
+        do_mcast            = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast bandwidth", CONFIG_BOOLEAN_AUTO);
+        do_bcast            = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "broadcast bandwidth", CONFIG_BOOLEAN_AUTO);
+        do_mcast_p          = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast packets", CONFIG_BOOLEAN_AUTO);
+        do_icmp             = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp", CONFIG_BOOLEAN_AUTO);
+        do_icmp_redir       = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp redirects", CONFIG_BOOLEAN_AUTO);
+        do_icmp_errors      = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp errors", CONFIG_BOOLEAN_AUTO);
+        do_icmp_echos       = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp echos", CONFIG_BOOLEAN_AUTO);
+        do_icmp_groupmemb   = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp group membership", CONFIG_BOOLEAN_AUTO);
+        do_icmp_router      = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp router", CONFIG_BOOLEAN_AUTO);
+        do_icmp_neighbor    = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp neighbor", CONFIG_BOOLEAN_AUTO);
+        do_icmp_mldv2       = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp mldv2", CONFIG_BOOLEAN_AUTO);
+        do_icmp_types       = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp types", CONFIG_BOOLEAN_AUTO);
+        do_ect              = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ect", CONFIG_BOOLEAN_AUTO);
 
         arl_base = arl_create("snmp6", NULL, 60);
         arl_expect(arl_base, "Ip6InReceives", &Ip6InReceives);
@@ -276,14 +276,15 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_bandwidth == CONFIG_ONDEMAND_YES || (do_bandwidth == CONFIG_ONDEMAND_ONDEMAND && (Ip6InOctets || Ip6OutOctets))) {
-        do_bandwidth = CONFIG_ONDEMAND_YES;
-        st = rrdset_find("system.ipv6");
+    if(do_bandwidth == CONFIG_BOOLEAN_YES || (do_bandwidth == CONFIG_BOOLEAN_AUTO && (Ip6InOctets || Ip6OutOctets))) {
+        do_bandwidth = CONFIG_BOOLEAN_YES;
+        st = rrdset_find_localhost("system.ipv6");
         if(unlikely(!st)) {
-            st = rrdset_create("system", "ipv6", NULL, "network", NULL, "IPv6 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA);
+            st = rrdset_create_localhost("system", "ipv6", NULL, "network", NULL, "IPv6 Bandwidth", "kilobits/s", 500
+                                         , update_every, RRDSET_TYPE_AREA);
 
-            rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "received", NULL, 8, 1024, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -8, 1024, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -294,16 +295,17 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_ip_packets == CONFIG_ONDEMAND_YES || (do_ip_packets == CONFIG_ONDEMAND_ONDEMAND && (Ip6InReceives || Ip6OutRequests || Ip6InDelivers || Ip6OutForwDatagrams))) {
-        do_ip_packets = CONFIG_ONDEMAND_YES;
-        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".packets");
+    if(do_ip_packets == CONFIG_BOOLEAN_YES || (do_ip_packets == CONFIG_BOOLEAN_AUTO && (Ip6InReceives || Ip6OutRequests || Ip6InDelivers || Ip6OutForwDatagrams))) {
+        do_ip_packets = CONFIG_BOOLEAN_YES;
+        st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".packets");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_SNMP6, "packets", NULL, "packets", NULL, "IPv6 Packets", "packets/s", 3000, update_every, RRDSET_TYPE_LINE);
+            st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "packets", NULL, "packets", NULL, "IPv6 Packets"
+                                         , "packets/s", 3000, update_every, RRDSET_TYPE_LINE);
 
-            rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "forwarded", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "delivers", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "forwarded", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "delivers", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -316,16 +318,17 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_ip_fragsout == CONFIG_ONDEMAND_YES || (do_ip_fragsout == CONFIG_ONDEMAND_ONDEMAND && (Ip6FragOKs || Ip6FragFails || Ip6FragCreates))) {
-        do_ip_fragsout = CONFIG_ONDEMAND_YES;
-        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".fragsout");
+    if(do_ip_fragsout == CONFIG_BOOLEAN_YES || (do_ip_fragsout == CONFIG_BOOLEAN_AUTO && (Ip6FragOKs || Ip6FragFails || Ip6FragCreates))) {
+        do_ip_fragsout = CONFIG_BOOLEAN_YES;
+        st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".fragsout");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_SNMP6, "fragsout", NULL, "fragments", NULL, "IPv6 Fragments Sent", "packets/s", 3010, update_every, RRDSET_TYPE_LINE);
-            st->isdetail = 1;
+            st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "fragsout", NULL, "fragments", NULL, "IPv6 Fragments Sent"
+                                         , "packets/s", 3010, update_every, RRDSET_TYPE_LINE);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-            rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "ok", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "failed", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "all", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -337,23 +340,25 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_ip_fragsin == CONFIG_ONDEMAND_YES || (do_ip_fragsin == CONFIG_ONDEMAND_ONDEMAND
+    if(do_ip_fragsin == CONFIG_BOOLEAN_YES || (do_ip_fragsin == CONFIG_BOOLEAN_AUTO
         && (
             Ip6ReasmOKs
             || Ip6ReasmFails
             || Ip6ReasmTimeout
             || Ip6ReasmReqds
             ))) {
-        do_ip_fragsin = CONFIG_ONDEMAND_YES;
-        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".fragsin");
+        do_ip_fragsin = CONFIG_BOOLEAN_YES;
+        st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".fragsin");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_SNMP6, "fragsin", NULL, "fragments", NULL, "IPv6 Fragments Reassembly", "packets/s", 3011, update_every, RRDSET_TYPE_LINE);
-            st->isdetail = 1;
-
-            rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "timeout", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "fragsin", NULL, "fragments", NULL
+                                         , "IPv6 Fragments Reassembly", "packets/s", 3011, update_every
+                                         , RRDSET_TYPE_LINE);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+            rrddim_add(st, "ok", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "failed", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "timeout", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "all", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -366,7 +371,7 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_ip_errors == CONFIG_ONDEMAND_YES || (do_ip_errors == CONFIG_ONDEMAND_ONDEMAND
+    if(do_ip_errors == CONFIG_BOOLEAN_YES || (do_ip_errors == CONFIG_BOOLEAN_AUTO
         && (
             Ip6InDiscards
             || Ip6OutDiscards
@@ -377,23 +382,24 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
             || Ip6InTruncatedPkts
             || Ip6InNoRoutes
         ))) {
-        do_ip_errors = CONFIG_ONDEMAND_YES;
-        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".errors");
+        do_ip_errors = CONFIG_BOOLEAN_YES;
+        st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".errors");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_SNMP6, "errors", NULL, "errors", NULL, "IPv6 Errors", "packets/s", 3002, update_every, RRDSET_TYPE_LINE);
-            st->isdetail = 1;
+            st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "errors", NULL, "errors", NULL, "IPv6 Errors", "packets/s"
+                                         , 3002, update_every, RRDSET_TYPE_LINE);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-            rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InDiscards", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "OutDiscards", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
 
-            rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InTooBigErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InTruncatedPkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InNoRoutes", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InTooBigErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InTruncatedPkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InNoRoutes", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
 
-            rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -413,14 +419,15 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_udp_packets == CONFIG_ONDEMAND_YES || (do_udp_packets == CONFIG_ONDEMAND_ONDEMAND && (Udp6InDatagrams || Udp6OutDatagrams))) {
-        do_udp_packets = CONFIG_ONDEMAND_YES;
-        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udppackets");
+    if(do_udp_packets == CONFIG_BOOLEAN_YES || (do_udp_packets == CONFIG_BOOLEAN_AUTO && (Udp6InDatagrams || Udp6OutDatagrams))) {
+        do_udp_packets = CONFIG_BOOLEAN_YES;
+        st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".udppackets");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_SNMP6, "udppackets", NULL, "udp", NULL, "IPv6 UDP Packets", "packets/s", 3601, update_every, RRDSET_TYPE_LINE);
+            st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "udppackets", NULL, "udp", NULL, "IPv6 UDP Packets"
+                                         , "packets/s", 3601, update_every, RRDSET_TYPE_LINE);
 
-            rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -431,7 +438,7 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_udp_errors == CONFIG_ONDEMAND_YES || (do_udp_errors == CONFIG_ONDEMAND_ONDEMAND
+    if(do_udp_errors == CONFIG_BOOLEAN_YES || (do_udp_errors == CONFIG_BOOLEAN_AUTO
         && (
             Udp6InErrors
             || Udp6NoPorts
@@ -440,18 +447,19 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
             || Udp6InCsumErrors
             || Udp6IgnoredMulti
         ))) {
-        do_udp_errors = CONFIG_ONDEMAND_YES;
-        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udperrors");
+        do_udp_errors = CONFIG_BOOLEAN_YES;
+        st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".udperrors");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_SNMP6, "udperrors", NULL, "udp", NULL, "IPv6 UDP Errors", "events/s", 3701, update_every, RRDSET_TYPE_LINE);
-            st->isdetail = 1;
-
-            rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "NoPorts", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "udperrors", NULL, "udp", NULL, "IPv6 UDP Errors"
+                                         , "events/s", 3701, update_every, RRDSET_TYPE_LINE);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+            rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "NoPorts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -466,14 +474,15 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_udplite_packets == CONFIG_ONDEMAND_YES || (do_udplite_packets == CONFIG_ONDEMAND_ONDEMAND && (UdpLite6InDatagrams || UdpLite6OutDatagrams))) {
-        do_udplite_packets = CONFIG_ONDEMAND_YES;
-        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udplitepackets");
+    if(do_udplite_packets == CONFIG_BOOLEAN_YES || (do_udplite_packets == CONFIG_BOOLEAN_AUTO && (UdpLite6InDatagrams || UdpLite6OutDatagrams))) {
+        do_udplite_packets = CONFIG_BOOLEAN_YES;
+        st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".udplitepackets");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_SNMP6, "udplitepackets", NULL, "udplite", NULL, "IPv6 UDPlite Packets", "packets/s", 3601, update_every, RRDSET_TYPE_LINE);
+            st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "udplitepackets", NULL, "udplite", NULL
+                                         , "IPv6 UDPlite Packets", "packets/s", 3601, update_every, RRDSET_TYPE_LINE);
 
-            rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -484,7 +493,7 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_udplite_errors == CONFIG_ONDEMAND_YES || (do_udplite_errors == CONFIG_ONDEMAND_ONDEMAND
+    if(do_udplite_errors == CONFIG_BOOLEAN_YES || (do_udplite_errors == CONFIG_BOOLEAN_AUTO
         && (
             UdpLite6InErrors
             || UdpLite6NoPorts
@@ -493,17 +502,18 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
             || Udp6InCsumErrors
             || UdpLite6InCsumErrors
         ))) {
-        do_udplite_errors = CONFIG_ONDEMAND_YES;
-        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udpliteerrors");
+        do_udplite_errors = CONFIG_BOOLEAN_YES;
+        st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".udpliteerrors");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_SNMP6, "udpliteerrors", NULL, "udplite", NULL, "IPv6 UDP Lite Errors", "events/s", 3701, update_every, RRDSET_TYPE_LINE);
-            st->isdetail = 1;
-
-            rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "NoPorts", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "udpliteerrors", NULL, "udplite", NULL
+                                         , "IPv6 UDP Lite Errors", "events/s", 3701, update_every, RRDSET_TYPE_LINE);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+            rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "NoPorts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -517,15 +527,17 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_mcast == CONFIG_ONDEMAND_YES || (do_mcast == CONFIG_ONDEMAND_ONDEMAND && (Ip6OutMcastOctets || Ip6InMcastOctets))) {
-        do_mcast = CONFIG_ONDEMAND_YES;
-        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".mcast");
+    if(do_mcast == CONFIG_BOOLEAN_YES || (do_mcast == CONFIG_BOOLEAN_AUTO && (Ip6OutMcastOctets || Ip6InMcastOctets))) {
+        do_mcast = CONFIG_BOOLEAN_YES;
+        st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".mcast");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_SNMP6, "mcast", NULL, "multicast", NULL, "IPv6 Multicast Bandwidth", "kilobits/s", 9000, update_every, RRDSET_TYPE_AREA);
-            st->isdetail = 1;
+            st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "mcast", NULL, "multicast", NULL
+                                         , "IPv6 Multicast Bandwidth", "kilobits/s", 9000, update_every
+                                         , RRDSET_TYPE_AREA);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-            rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "received", NULL, 8, 1024, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -8, 1024, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -536,15 +548,17 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_bcast == CONFIG_ONDEMAND_YES || (do_bcast == CONFIG_ONDEMAND_ONDEMAND && (Ip6OutBcastOctets || Ip6InBcastOctets))) {
-        do_bcast = CONFIG_ONDEMAND_YES;
-        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".bcast");
+    if(do_bcast == CONFIG_BOOLEAN_YES || (do_bcast == CONFIG_BOOLEAN_AUTO && (Ip6OutBcastOctets || Ip6InBcastOctets))) {
+        do_bcast = CONFIG_BOOLEAN_YES;
+        st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".bcast");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_SNMP6, "bcast", NULL, "broadcast", NULL, "IPv6 Broadcast Bandwidth", "kilobits/s", 8000, update_every, RRDSET_TYPE_AREA);
-            st->isdetail = 1;
+            st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "bcast", NULL, "broadcast", NULL
+                                         , "IPv6 Broadcast Bandwidth", "kilobits/s", 8000, update_every
+                                         , RRDSET_TYPE_AREA);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-            rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "received", NULL, 8, 1024, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -8, 1024, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -555,15 +569,16 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_mcast_p == CONFIG_ONDEMAND_YES || (do_mcast_p == CONFIG_ONDEMAND_ONDEMAND && (Ip6OutMcastPkts || Ip6InMcastPkts))) {
-        do_mcast_p = CONFIG_ONDEMAND_YES;
-        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".mcastpkts");
+    if(do_mcast_p == CONFIG_BOOLEAN_YES || (do_mcast_p == CONFIG_BOOLEAN_AUTO && (Ip6OutMcastPkts || Ip6InMcastPkts))) {
+        do_mcast_p = CONFIG_BOOLEAN_YES;
+        st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".mcastpkts");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_SNMP6, "mcastpkts", NULL, "multicast", NULL, "IPv6 Multicast Packets", "packets/s", 9500, update_every, RRDSET_TYPE_LINE);
-            st->isdetail = 1;
+            st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "mcastpkts", NULL, "multicast", NULL
+                                         , "IPv6 Multicast Packets", "packets/s", 9500, update_every, RRDSET_TYPE_LINE);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-            rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -574,14 +589,15 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_icmp == CONFIG_ONDEMAND_YES || (do_icmp == CONFIG_ONDEMAND_ONDEMAND && (Icmp6InMsgs || Icmp6OutMsgs))) {
-        do_icmp = CONFIG_ONDEMAND_YES;
-        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmp");
+    if(do_icmp == CONFIG_BOOLEAN_YES || (do_icmp == CONFIG_BOOLEAN_AUTO && (Icmp6InMsgs || Icmp6OutMsgs))) {
+        do_icmp = CONFIG_BOOLEAN_YES;
+        st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".icmp");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmp", NULL, "icmp", NULL, "IPv6 ICMP Messages", "messages/s", 10000, update_every, RRDSET_TYPE_LINE);
+            st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "icmp", NULL, "icmp", NULL, "IPv6 ICMP Messages"
+                                         , "messages/s", 10000, update_every, RRDSET_TYPE_LINE);
 
-            rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -592,14 +608,15 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_icmp_redir == CONFIG_ONDEMAND_YES || (do_icmp_redir == CONFIG_ONDEMAND_ONDEMAND && (Icmp6InRedirects || Icmp6OutRedirects))) {
-        do_icmp_redir = CONFIG_ONDEMAND_YES;
-        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpredir");
+    if(do_icmp_redir == CONFIG_BOOLEAN_YES || (do_icmp_redir == CONFIG_BOOLEAN_AUTO && (Icmp6InRedirects || Icmp6OutRedirects))) {
+        do_icmp_redir = CONFIG_BOOLEAN_YES;
+        st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".icmpredir");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpredir", NULL, "icmp", NULL, "IPv6 ICMP Redirects", "redirects/s", 10050, update_every, RRDSET_TYPE_LINE);
+            st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "icmpredir", NULL, "icmp", NULL, "IPv6 ICMP Redirects"
+                                         , "redirects/s", 10050, update_every, RRDSET_TYPE_LINE);
 
-            rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -610,7 +627,7 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_icmp_errors == CONFIG_ONDEMAND_YES || (do_icmp_errors == CONFIG_ONDEMAND_ONDEMAND
+    if(do_icmp_errors == CONFIG_BOOLEAN_YES || (do_icmp_errors == CONFIG_BOOLEAN_AUTO
         && (
             Icmp6InErrors
             || Icmp6OutErrors
@@ -624,23 +641,24 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
             || Icmp6OutTimeExcds
             || Icmp6OutParmProblems
         ))) {
-        do_icmp_errors = CONFIG_ONDEMAND_YES;
-        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmperrors");
+        do_icmp_errors = CONFIG_BOOLEAN_YES;
+        st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".icmperrors");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmperrors", NULL, "icmp", NULL, "IPv6 ICMP Errors", "errors/s", 10100, update_every, RRDSET_TYPE_LINE);
-
-            rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "OutErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
-
-            rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InDestUnreachs", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InPktTooBigs", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InTimeExcds", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InParmProblems", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "OutDestUnreachs", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "OutPktTooBigs", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "OutTimeExcds", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "OutParmProblems", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "icmperrors", NULL, "icmp", NULL, "IPv6 ICMP Errors"
+                                         , "errors/s", 10100, update_every, RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "OutErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+
+            rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InDestUnreachs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InPktTooBigs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InTimeExcds", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InParmProblems", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "OutDestUnreachs", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "OutPktTooBigs", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "OutTimeExcds", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "OutParmProblems", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -660,22 +678,23 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_icmp_echos == CONFIG_ONDEMAND_YES || (do_icmp_echos == CONFIG_ONDEMAND_ONDEMAND
+    if(do_icmp_echos == CONFIG_BOOLEAN_YES || (do_icmp_echos == CONFIG_BOOLEAN_AUTO
         && (
             Icmp6InEchos
             || Icmp6OutEchos
             || Icmp6InEchoReplies
             || Icmp6OutEchoReplies
         ))) {
-        do_icmp_echos = CONFIG_ONDEMAND_YES;
-        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpechos");
+        do_icmp_echos = CONFIG_BOOLEAN_YES;
+        st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".icmpechos");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpechos", NULL, "icmp", NULL, "IPv6 ICMP Echo", "messages/s", 10200, update_every, RRDSET_TYPE_LINE);
+            st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "icmpechos", NULL, "icmp", NULL, "IPv6 ICMP Echo"
+                                         , "messages/s", 10200, update_every, RRDSET_TYPE_LINE);
 
-            rrddim_add(st, "InEchos", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "OutEchos", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InEchoReplies", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "OutEchoReplies", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InEchos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "OutEchos", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InEchoReplies", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "OutEchoReplies", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -688,7 +707,7 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_icmp_groupmemb == CONFIG_ONDEMAND_YES || (do_icmp_groupmemb == CONFIG_ONDEMAND_ONDEMAND
+    if(do_icmp_groupmemb == CONFIG_BOOLEAN_YES || (do_icmp_groupmemb == CONFIG_BOOLEAN_AUTO
         && (
             Icmp6InGroupMembQueries
             || Icmp6OutGroupMembQueries
@@ -697,17 +716,19 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
             || Icmp6InGroupMembReductions
             || Icmp6OutGroupMembReductions
         ))) {
-        do_icmp_groupmemb = CONFIG_ONDEMAND_YES;
-        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".groupmemb");
+        do_icmp_groupmemb = CONFIG_BOOLEAN_YES;
+        st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".groupmemb");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_SNMP6, "groupmemb", NULL, "icmp", NULL, "IPv6 ICMP Group Membership", "messages/s", 10300, update_every, RRDSET_TYPE_LINE);
-
-            rrddim_add(st, "InQueries", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "OutQueries", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InResponses", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "OutResponses", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InReductions", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "OutReductions", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "groupmemb", NULL, "icmp", NULL
+                                         , "IPv6 ICMP Group Membership", "messages/s", 10300, update_every
+                                         , RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "InQueries", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "OutQueries", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InResponses", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "OutResponses", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InReductions", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "OutReductions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -722,22 +743,23 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_icmp_router == CONFIG_ONDEMAND_YES || (do_icmp_router == CONFIG_ONDEMAND_ONDEMAND
+    if(do_icmp_router == CONFIG_BOOLEAN_YES || (do_icmp_router == CONFIG_BOOLEAN_AUTO
         && (
             Icmp6InRouterSolicits
             || Icmp6OutRouterSolicits
             || Icmp6InRouterAdvertisements
             || Icmp6OutRouterAdvertisements
         ))) {
-        do_icmp_router = CONFIG_ONDEMAND_YES;
-        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmprouter");
+        do_icmp_router = CONFIG_BOOLEAN_YES;
+        st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".icmprouter");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmprouter", NULL, "icmp", NULL, "IPv6 Router Messages", "messages/s", 10400, update_every, RRDSET_TYPE_LINE);
+            st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "icmprouter", NULL, "icmp", NULL, "IPv6 Router Messages"
+                                         , "messages/s", 10400, update_every, RRDSET_TYPE_LINE);
 
-            rrddim_add(st, "InSolicits", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "OutSolicits", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InSolicits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "OutSolicits", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -750,22 +772,24 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_icmp_neighbor == CONFIG_ONDEMAND_YES || (do_icmp_neighbor == CONFIG_ONDEMAND_ONDEMAND
+    if(do_icmp_neighbor == CONFIG_BOOLEAN_YES || (do_icmp_neighbor == CONFIG_BOOLEAN_AUTO
         && (
             Icmp6InNeighborSolicits
             || Icmp6OutNeighborSolicits
             || Icmp6InNeighborAdvertisements
             || Icmp6OutNeighborAdvertisements
         ))) {
-        do_icmp_neighbor = CONFIG_ONDEMAND_YES;
-        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpneighbor");
+        do_icmp_neighbor = CONFIG_BOOLEAN_YES;
+        st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".icmpneighbor");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpneighbor", NULL, "icmp", NULL, "IPv6 Neighbor Messages", "messages/s", 10500, update_every, RRDSET_TYPE_LINE);
-
-            rrddim_add(st, "InSolicits", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "OutSolicits", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "icmpneighbor", NULL, "icmp", NULL
+                                         , "IPv6 Neighbor Messages", "messages/s", 10500, update_every
+                                         , RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "InSolicits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "OutSolicits", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -778,14 +802,15 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_icmp_mldv2 == CONFIG_ONDEMAND_YES || (do_icmp_mldv2 == CONFIG_ONDEMAND_ONDEMAND && (Icmp6InMLDv2Reports || Icmp6OutMLDv2Reports))) {
-        do_icmp_mldv2 = CONFIG_ONDEMAND_YES;
-        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpmldv2");
+    if(do_icmp_mldv2 == CONFIG_BOOLEAN_YES || (do_icmp_mldv2 == CONFIG_BOOLEAN_AUTO && (Icmp6InMLDv2Reports || Icmp6OutMLDv2Reports))) {
+        do_icmp_mldv2 = CONFIG_BOOLEAN_YES;
+        st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".icmpmldv2");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpmldv2", NULL, "icmp", NULL, "IPv6 ICMP MLDv2 Reports", "reports/s", 10600, update_every, RRDSET_TYPE_LINE);
+            st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "icmpmldv2", NULL, "icmp", NULL, "IPv6 ICMP MLDv2 Reports"
+                                         , "reports/s", 10600, update_every, RRDSET_TYPE_LINE);
 
-            rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -796,7 +821,7 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_icmp_types == CONFIG_ONDEMAND_YES || (do_icmp_types == CONFIG_ONDEMAND_ONDEMAND
+    if(do_icmp_types == CONFIG_BOOLEAN_YES || (do_icmp_types == CONFIG_BOOLEAN_AUTO
         && (
             Icmp6InType1
             || Icmp6InType128
@@ -809,21 +834,22 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
             || Icmp6OutType135
             || Icmp6OutType143
         ))) {
-        do_icmp_types = CONFIG_ONDEMAND_YES;
-        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmptypes");
+        do_icmp_types = CONFIG_BOOLEAN_YES;
+        st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".icmptypes");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmptypes", NULL, "icmp", NULL, "IPv6 ICMP Types", "messages/s", 10700, update_every, RRDSET_TYPE_LINE);
-
-            rrddim_add(st, "InType1", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InType128", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InType129", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InType136", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "OutType1", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "OutType128", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "OutType129", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "OutType133", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "OutType135", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "OutType143", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "icmptypes", NULL, "icmp", NULL, "IPv6 ICMP Types"
+                                         , "messages/s", 10700, update_every, RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "InType1", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InType128", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InType129", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InType136", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "OutType1", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "OutType128", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "OutType129", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "OutType133", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "OutType135", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "OutType143", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -842,22 +868,23 @@ int do_proc_net_snmp6(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_ect == CONFIG_ONDEMAND_YES || (do_ect == CONFIG_ONDEMAND_ONDEMAND
+    if(do_ect == CONFIG_BOOLEAN_YES || (do_ect == CONFIG_BOOLEAN_AUTO
         && (
             Ip6InNoECTPkts
             || Ip6InECT1Pkts
             || Ip6InECT0Pkts
             || Ip6InCEPkts
         ))) {
-        do_ect = CONFIG_ONDEMAND_YES;
-        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".ect");
+        do_ect = CONFIG_BOOLEAN_YES;
+        st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".ect");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_SNMP6, "ect", NULL, "packets", NULL, "IPv6 ECT Packets", "packets/s", 10800, update_every, RRDSET_TYPE_LINE);
+            st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "ect", NULL, "packets", NULL, "IPv6 ECT Packets"
+                                         , "packets/s", 10800, update_every, RRDSET_TYPE_LINE);
 
-            rrddim_add(st, "InNoECTPkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InECT1Pkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InECT0Pkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "InCEPkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InNoECTPkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InECT1Pkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InECT0Pkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "InCEPkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
index ed6d8db7ac357be945b0acd729626f0ac2073dbf..40946a7a5c6fe1a7139d431f1a4c35362502bda7 100644 (file)
@@ -77,12 +77,13 @@ int do_proc_net_softnet_stat(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    st = rrdset_find_bytype("system", "softnet_stat");
+    st = rrdset_find_bytype_localhost("system", "softnet_stat");
     if(unlikely(!st)) {
-        st = rrdset_create("system", "softnet_stat", NULL, "softnet_stat", NULL, "System softnet_stat", "events/s", 955, update_every, RRDSET_TYPE_LINE);
+        st = rrdset_create_localhost("system", "softnet_stat", NULL, "softnet_stat", NULL, "System softnet_stat"
+                                     , "events/s", 955, update_every, RRDSET_TYPE_LINE);
         for(w = 0; w < allocated_columns ;w++)
             if(unlikely(softnet_column_name(w)))
-                rrddim_add(st, softnet_column_name(w), NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, softnet_column_name(w), NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
     }
     else rrdset_next(st);
 
@@ -97,15 +98,16 @@ int do_proc_net_softnet_stat(int update_every, usec_t dt) {
             char id[50+1];
             snprintfz(id, 50, "cpu%zu_softnet_stat", l);
 
-            st = rrdset_find_bytype("cpu", id);
+            st = rrdset_find_bytype_localhost("cpu", id);
             if(unlikely(!st)) {
                 char title[100+1];
                 snprintfz(title, 100, "CPU%zu softnet_stat", l);
 
-                st = rrdset_create("cpu", id, NULL, "softnet_stat", NULL, title, "events/s", 4101 + l, update_every, RRDSET_TYPE_LINE);
+                st = rrdset_create_localhost("cpu", id, NULL, "softnet_stat", NULL, title, "events/s", 4101 + l
+                                             , update_every, RRDSET_TYPE_LINE);
                 for(w = 0; w < allocated_columns ;w++)
                     if(unlikely(softnet_column_name(w)))
-                        rrddim_add(st, softnet_column_name(w), NULL, 1, 1, RRDDIM_INCREMENTAL);
+                        rrddim_add(st, softnet_column_name(w), NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
             }
             else rrdset_next(st);
 
index 7a90683bb9bd800603448ab12f54f57dab12e2e7..e04b80a3eb3bbfabff17f1cec9537767c4709505 100644 (file)
@@ -47,7 +47,7 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) {
         if(!do_sockets && !read_full)
             return 1;
 
-        rrdvar_max = rrdvar_custom_host_variable_create(&localhost, "netfilter.conntrack.max");
+        rrdvar_max = rrdvar_custom_host_variable_create(localhost, "netfilter.conntrack.max");
     }
 
     if(likely(read_full)) {
@@ -130,11 +130,13 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(do_sockets) {
-        st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_sockets");
+        st = rrdset_find_localhost(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_sockets");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_sockets", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Connections", "active connections", 3000, update_every, RRDSET_TYPE_LINE);
+            st = rrdset_create_localhost(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_sockets", NULL
+                                         , RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Connections"
+                                         , "active connections", 3000, update_every, RRDSET_TYPE_LINE);
 
-            rrddim_add(st, "connections", NULL, 1, 1, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "connections", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
         }
         else rrdset_next(st);
 
@@ -145,13 +147,15 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(do_new) {
-        st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_new");
+        st = rrdset_find_localhost(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_new");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_new", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker New Connections", "connections/s", 3001, update_every, RRDSET_TYPE_LINE);
+            st = rrdset_create_localhost(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_new", NULL
+                                         , RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker New Connections"
+                                         , "connections/s", 3001, update_every, RRDSET_TYPE_LINE);
 
-            rrddim_add(st, "new", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "ignore", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "invalid", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "new", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "ignore", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "invalid", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -164,14 +168,16 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(do_changes) {
-        st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_changes");
+        st = rrdset_find_localhost(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_changes");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_changes", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Changes", "changes/s", 3002, update_every, RRDSET_TYPE_LINE);
-            st->isdetail = 1;
-
-            rrddim_add(st, "inserted", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "deleted", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "delete_list", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            st = rrdset_create_localhost(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_changes", NULL
+                                         , RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Changes", "changes/s"
+                                         , 3002, update_every, RRDSET_TYPE_LINE);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+            rrddim_add(st, "inserted", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "deleted", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "delete_list", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -184,14 +190,16 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(do_expect) {
-        st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_expect");
+        st = rrdset_find_localhost(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_expect");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_expect", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Expectations", "expectations/s", 3003, update_every, RRDSET_TYPE_LINE);
-            st->isdetail = 1;
-
-            rrddim_add(st, "created", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "deleted", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "new", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            st = rrdset_create_localhost(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_expect", NULL
+                                         , RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Expectations"
+                                         , "expectations/s", 3003, update_every, RRDSET_TYPE_LINE);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+            rrddim_add(st, "created", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "deleted", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "new", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -204,14 +212,16 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(do_search) {
-        st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_search");
+        st = rrdset_find_localhost(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_search");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_search", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Searches", "searches/s", 3010, update_every, RRDSET_TYPE_LINE);
-            st->isdetail = 1;
-
-            rrddim_add(st, "searched", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "restarted", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "found", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            st = rrdset_create_localhost(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_search", NULL
+                                         , RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Searches"
+                                         , "searches/s", 3010, update_every, RRDSET_TYPE_LINE);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+            rrddim_add(st, "searched", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "restarted", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "found", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -224,15 +234,17 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(do_errors) {
-        st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_errors");
+        st = rrdset_find_localhost(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_errors");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_errors", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Errors", "events/s", 3005, update_every, RRDSET_TYPE_LINE);
-            st->isdetail = 1;
-
-            rrddim_add(st, "icmp_error", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "insert_failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "drop", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "early_drop", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            st = rrdset_create_localhost(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_errors", NULL
+                                         , RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Errors", "events/s"
+                                         , 3005, update_every, RRDSET_TYPE_LINE);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+            rrddim_add(st, "icmp_error", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "insert_failed", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "drop", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "early_drop", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
index 688e8c7cea1d80416f5f1e44e47e17befca86c1d..5a1fc30eb7f2bd1872817e7dbdd63ac35f571e8f 100644 (file)
@@ -10,10 +10,10 @@ int do_proc_net_stat_synproxy(int update_every, usec_t dt) {
     static procfile *ff = NULL;
 
     if(unlikely(do_entries == -1)) {
-        do_entries  = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY entries", CONFIG_ONDEMAND_ONDEMAND);
-        do_cookies  = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY cookies", CONFIG_ONDEMAND_ONDEMAND);
-        do_syns     = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY SYN received", CONFIG_ONDEMAND_ONDEMAND);
-        do_reopened = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY connections reopened", CONFIG_ONDEMAND_ONDEMAND);
+        do_entries  = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY entries", CONFIG_BOOLEAN_AUTO);
+        do_cookies  = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY cookies", CONFIG_BOOLEAN_AUTO);
+        do_syns     = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY SYN received", CONFIG_BOOLEAN_AUTO);
+        do_reopened = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY connections reopened", CONFIG_BOOLEAN_AUTO);
     }
 
     if(unlikely(!ff)) {
@@ -57,14 +57,16 @@ int do_proc_net_stat_synproxy(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if((do_entries == CONFIG_ONDEMAND_ONDEMAND && events) || do_entries == CONFIG_ONDEMAND_YES) {
-        do_entries = CONFIG_ONDEMAND_YES;
+    if((do_entries == CONFIG_BOOLEAN_AUTO && events) || do_entries == CONFIG_BOOLEAN_YES) {
+        do_entries = CONFIG_BOOLEAN_YES;
 
-        st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_entries");
+        st = rrdset_find_localhost(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_entries");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_entries", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY Entries Used", "entries", 3304, update_every, RRDSET_TYPE_LINE);
+            st = rrdset_create_localhost(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_entries", NULL
+                                         , RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY Entries Used", "entries", 3304
+                                         , update_every, RRDSET_TYPE_LINE);
 
-            rrddim_add(st, "entries", NULL, 1, 1, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "entries", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
         }
         else rrdset_next(st);
 
@@ -74,14 +76,16 @@ int do_proc_net_stat_synproxy(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if((do_syns == CONFIG_ONDEMAND_ONDEMAND && events) || do_syns == CONFIG_ONDEMAND_YES) {
-        do_syns = CONFIG_ONDEMAND_YES;
+    if((do_syns == CONFIG_BOOLEAN_AUTO && events) || do_syns == CONFIG_BOOLEAN_YES) {
+        do_syns = CONFIG_BOOLEAN_YES;
 
-        st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_syn_received");
+        st = rrdset_find_localhost(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_syn_received");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_syn_received", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY SYN Packets received", "SYN/s", 3301, update_every, RRDSET_TYPE_LINE);
+            st = rrdset_create_localhost(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_syn_received", NULL
+                                         , RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY SYN Packets received", "SYN/s"
+                                         , 3301, update_every, RRDSET_TYPE_LINE);
 
-            rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -91,14 +95,16 @@ int do_proc_net_stat_synproxy(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if((do_reopened == CONFIG_ONDEMAND_ONDEMAND && events) || do_reopened == CONFIG_ONDEMAND_YES) {
-        do_reopened = CONFIG_ONDEMAND_YES;
+    if((do_reopened == CONFIG_BOOLEAN_AUTO && events) || do_reopened == CONFIG_BOOLEAN_YES) {
+        do_reopened = CONFIG_BOOLEAN_YES;
 
-        st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_conn_reopened");
+        st = rrdset_find_localhost(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_conn_reopened");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_conn_reopened", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY Connections Reopened", "connections/s", 3303, update_every, RRDSET_TYPE_LINE);
+            st = rrdset_create_localhost(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_conn_reopened", NULL
+                                         , RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY Connections Reopened"
+                                         , "connections/s", 3303, update_every, RRDSET_TYPE_LINE);
 
-            rrddim_add(st, "reopened", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "reopened", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -108,16 +114,18 @@ int do_proc_net_stat_synproxy(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if((do_cookies == CONFIG_ONDEMAND_ONDEMAND && events) || do_cookies == CONFIG_ONDEMAND_YES) {
-        do_cookies = CONFIG_ONDEMAND_YES;
+    if((do_cookies == CONFIG_BOOLEAN_AUTO && events) || do_cookies == CONFIG_BOOLEAN_YES) {
+        do_cookies = CONFIG_BOOLEAN_YES;
 
-        st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_cookies");
+        st = rrdset_find_localhost(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_cookies");
         if(unlikely(!st)) {
-            st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_cookies", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY TCP Cookies", "cookies/s", 3302, update_every, RRDSET_TYPE_LINE);
+            st = rrdset_create_localhost(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_cookies", NULL
+                                         , RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY TCP Cookies", "cookies/s", 3302
+                                         , update_every, RRDSET_TYPE_LINE);
 
-            rrddim_add(st, "valid", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "invalid", NULL, -1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st, "retransmits", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "valid", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "invalid", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st, "retransmits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
index 780c2af7af60b6e52932387667cb7dab4ac5e128..bb031a9ab40e808b0c8f26082806ac48cd5e6b9c 100644 (file)
@@ -293,7 +293,7 @@ struct mountinfo *mountinfo_read(int do_statvfs) {
 
                     struct mountinfo *mt;
                     for(mt = root; mt; mt = mt->next) {
-                        if(unlikely(mt->st_dev == mi->st_dev && !(mi->flags & MOUNTINFO_NO_STAT))) {
+                        if(unlikely(mt->st_dev == mi->st_dev && !(mt->flags & MOUNTINFO_IS_SAME_DEV))) {
                             if(strlen(mi->mount_point) < strlen(mt->mount_point))
                                 mt->flags |= MOUNTINFO_IS_SAME_DEV;
                             else
@@ -319,7 +319,7 @@ struct mountinfo *mountinfo_read(int do_statvfs) {
         }
 
         // check if it has size
-        if(do_statvfs) {
+        if(do_statvfs && !(mi->flags & MOUNTINFO_IS_DUMMY)) {
             struct statvfs buff_statvfs;
             if(unlikely(statvfs(mi->mount_point, &buff_statvfs) < 0)) {
                 mi->flags |= MOUNTINFO_NO_STAT;
index 9eb72cd376d5ca2f49dcd8224b08cf5fd99165f4..560e2acb2d9dd1ab67c3e14f88cfd399e0df205b 100644 (file)
@@ -128,8 +128,9 @@ int do_proc_softirqs(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    st = rrdset_find_bytype("system", "softirqs");
-    if(unlikely(!st)) st = rrdset_create("system", "softirqs", NULL, "softirqs", NULL, "System softirqs", "softirqs/s", 950, update_every, RRDSET_TYPE_STACKED);
+    st = rrdset_find_bytype_localhost("system", "softirqs");
+    if(unlikely(!st)) st = rrdset_create_localhost("system", "softirqs", NULL, "softirqs", NULL, "System softirqs"
+                                                   , "softirqs/s", 950, update_every, RRDSET_TYPE_STACKED);
     else rrdset_next(st);
 
     for(l = 0; l < lines ;l++) {
@@ -141,7 +142,7 @@ int do_proc_softirqs(int update_every, usec_t dt) {
         if(unlikely(!irr->rd || strncmp(irr->name, irr->rd->name, MAX_INTERRUPT_NAME) != 0)) {
             irr->rd = rrddim_find(st, irr->id);
             if(unlikely(!irr->rd))
-                irr->rd = rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL);
+                irr->rd = rrddim_add(st, irr->id, irr->name, 1, 1, RRD_ALGORITHM_INCREMENTAL);
             else
                 rrddim_set_name(st, irr->rd, irr->name);
 
@@ -163,7 +164,7 @@ int do_proc_softirqs(int update_every, usec_t dt) {
             char id[50+1];
             snprintfz(id, 50, "cpu%d_softirqs", c);
 
-            st = rrdset_find_bytype("cpu", id);
+            st = rrdset_find_bytype_localhost("cpu", id);
             if(unlikely(!st)) {
                 // find if everything is zero
                 unsigned long long core_sum = 0 ;
@@ -176,7 +177,8 @@ int do_proc_softirqs(int update_every, usec_t dt) {
 
                 char title[100+1];
                 snprintfz(title, 100, "CPU%d softirqs", c);
-                st = rrdset_create("cpu", id, NULL, "softirqs", "cpu.softirqs", title, "softirqs/s", 3000 + c, update_every, RRDSET_TYPE_STACKED);
+                st = rrdset_create_localhost("cpu", id, NULL, "softirqs", "cpu.softirqs", title, "softirqs/s", 3000 + c
+                                             , update_every, RRDSET_TYPE_STACKED);
             }
             else rrdset_next(st);
 
@@ -186,7 +188,7 @@ int do_proc_softirqs(int update_every, usec_t dt) {
                 if(unlikely(!irr->cpu[c].rd)) {
                     irr->cpu[c].rd = rrddim_find(st, irr->id);
                     if(unlikely(!irr->cpu[c].rd))
-                        irr->cpu[c].rd = rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL);
+                        irr->cpu[c].rd = rrddim_add(st, irr->id, irr->name, 1, 1, RRD_ALGORITHM_INCREMENTAL);
                     else
                         rrddim_set_name(st, irr->cpu[c].rd, irr->name);
                 }
index d52de1b26ebafcf2a33e726fcc33ec73b1726afb..04f0896cd20266f1ae120350334744e4afb3122d 100644 (file)
@@ -91,24 +91,25 @@ int do_proc_stat(int update_every, usec_t dt) {
             }
 
             if(likely((isthistotal && do_cpu) || (!isthistotal && do_cpu_cores))) {
-                st = rrdset_find_bytype(type, id);
+                st = rrdset_find_bytype_localhost(type, id);
                 if(unlikely(!st)) {
-                    st = rrdset_create(type, id, NULL, family, context, title, "percentage", priority, update_every, RRDSET_TYPE_STACKED);
+                    st = rrdset_create_localhost(type, id, NULL, family, context, title, "percentage", priority
+                                                 , update_every, RRDSET_TYPE_STACKED);
 
                     long multiplier = 1;
                     long divisor = 1; // sysconf(_SC_CLK_TCK);
 
-                    rrddim_add(st, "guest_nice", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                    rrddim_add(st, "guest", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                    rrddim_add(st, "steal", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                    rrddim_add(st, "softirq", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                    rrddim_add(st, "irq", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                    rrddim_add(st, "user", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                    rrddim_add(st, "system", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                    rrddim_add(st, "nice", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                    rrddim_add(st, "iowait", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-
-                    rrddim_add(st, "idle", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "guest_nice", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "guest", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "steal", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "softirq", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "irq", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "user", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "system", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "nice", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "iowait", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
+
+                    rrddim_add(st, "idle", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
                     rrddim_hide(st, "idle");
                 }
                 else rrdset_next(st);
@@ -132,12 +133,13 @@ int do_proc_stat(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if(likely(do_interrupts)) {
-                st = rrdset_find_bytype("system", "intr");
+                st = rrdset_find_bytype_localhost("system", "intr");
                 if(unlikely(!st)) {
-                    st = rrdset_create("system", "intr", NULL, "interrupts", NULL, "CPU Interrupts", "interrupts/s", 900, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
+                    st = rrdset_create_localhost("system", "intr", NULL, "interrupts", NULL, "CPU Interrupts"
+                                                 , "interrupts/s", 900, update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-                    rrddim_add(st, "interrupts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "interrupts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -151,11 +153,12 @@ int do_proc_stat(int update_every, usec_t dt) {
             // --------------------------------------------------------------------
 
             if(likely(do_context)) {
-                st = rrdset_find_bytype("system", "ctxt");
+                st = rrdset_find_bytype_localhost("system", "ctxt");
                 if(unlikely(!st)) {
-                    st = rrdset_create("system", "ctxt", NULL, "processes", NULL, "CPU Context Switches", "context switches/s", 800, update_every, RRDSET_TYPE_LINE);
+                    st = rrdset_create_localhost("system", "ctxt", NULL, "processes", NULL, "CPU Context Switches"
+                                                 , "context switches/s", 800, update_every, RRDSET_TYPE_LINE);
 
-                    rrddim_add(st, "switches", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "switches", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
                 }
                 else rrdset_next(st);
 
@@ -177,12 +180,13 @@ int do_proc_stat(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(likely(do_forks)) {
-        st = rrdset_find_bytype("system", "forks");
+        st = rrdset_find_bytype_localhost("system", "forks");
         if(unlikely(!st)) {
-            st = rrdset_create("system", "forks", NULL, "processes", NULL, "Started Processes", "processes/s", 700, update_every, RRDSET_TYPE_LINE);
-            st->isdetail = 1;
+            st = rrdset_create_localhost("system", "forks", NULL, "processes", NULL, "Started Processes", "processes/s"
+                                         , 700, update_every, RRDSET_TYPE_LINE);
+            rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
 
-            rrddim_add(st, "started", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "started", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st);
 
@@ -193,12 +197,13 @@ int do_proc_stat(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(likely(do_processes)) {
-        st = rrdset_find_bytype("system", "processes");
+        st = rrdset_find_bytype_localhost("system", "processes");
         if(unlikely(!st)) {
-            st = rrdset_create("system", "processes", NULL, "processes", NULL, "System Processes", "processes", 600, update_every, RRDSET_TYPE_LINE);
+            st = rrdset_create_localhost("system", "processes", NULL, "processes", NULL, "System Processes", "processes"
+                                         , 600, update_every, RRDSET_TYPE_LINE);
 
-            rrddim_add(st, "running", NULL, 1, 1, RRDDIM_ABSOLUTE);
-            rrddim_add(st, "blocked", NULL, -1, 1, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "running", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+            rrddim_add(st, "blocked", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE);
         }
         else rrdset_next(st);
 
index 25dbca839025ed55205aea33208b5fb50dfdbc18..fea8900d33cc7ef0c89fbc3a81e3da4b1e9db575 100644 (file)
@@ -17,10 +17,11 @@ int do_proc_sys_kernel_random_entropy_avail(int update_every, usec_t dt) {
 
     unsigned long long entropy = str2ull(procfile_lineword(ff, 0, 0));
 
-    RRDSET *st = rrdset_find_bytype("system", "entropy");
+    RRDSET *st = rrdset_find_bytype_localhost("system", "entropy");
     if(unlikely(!st)) {
-        st = rrdset_create("system", "entropy", NULL, "entropy", NULL, "Available Entropy", "entropy", 1000, update_every, RRDSET_TYPE_LINE);
-        rrddim_add(st, "entropy", NULL, 1, 1, RRDDIM_ABSOLUTE);
+        st = rrdset_create_localhost("system", "entropy", NULL, "entropy", NULL, "Available Entropy", "entropy", 1000
+                                     , update_every, RRDSET_TYPE_LINE);
+        rrddim_add(st, "entropy", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
     }
     else rrdset_next(st);
 
index 8dd10b1f0bfe14a57acc5b2841a4c8e264288228..f74cccb9784c980c602431eeae4d62fd9359362f 100644 (file)
@@ -39,11 +39,12 @@ int do_proc_uptime(int update_every, usec_t dt) {
     // --------------------------------------------------------------------
 
     if(unlikely(!st))
-        st = rrdset_find("system.uptime");
+        st = rrdset_find_localhost("system.uptime");
 
     if(unlikely(!st)) {
-        st = rrdset_create("system", "uptime", NULL, "uptime", NULL, "System Uptime", "seconds", 1000, update_every, RRDSET_TYPE_LINE);
-        rrddim_add(st, "uptime", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+        st = rrdset_create_localhost("system", "uptime", NULL, "uptime", NULL, "System Uptime", "seconds", 1000
+                                     , update_every, RRDSET_TYPE_LINE);
+        rrddim_add(st, "uptime", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
     }
     else rrdset_next(st);
 
index 748fa3486da5fb7751cc800ea1ff7ec549929798..847487363e6b7218cb2cbc1b6ef4e9d463bcb216 100644 (file)
@@ -25,10 +25,10 @@ int do_proc_vmstat(int update_every, usec_t dt) {
     static unsigned long long pswpout = 0ULL;
 
     if(unlikely(!arl_base)) {
-        do_swapio = config_get_boolean_ondemand("plugin:proc:/proc/vmstat", "swap i/o", CONFIG_ONDEMAND_ONDEMAND);
+        do_swapio = config_get_boolean_ondemand("plugin:proc:/proc/vmstat", "swap i/o", CONFIG_BOOLEAN_AUTO);
         do_io = config_get_boolean("plugin:proc:/proc/vmstat", "disk i/o", 1);
         do_pgfaults = config_get_boolean("plugin:proc:/proc/vmstat", "memory page faults", 1);
-        do_numa = config_get_boolean_ondemand("plugin:proc:/proc/vmstat", "system-wide numa metric summary", CONFIG_ONDEMAND_ONDEMAND);
+        do_numa = config_get_boolean_ondemand("plugin:proc:/proc/vmstat", "system-wide numa metric summary", CONFIG_BOOLEAN_AUTO);
 
 
         arl_base = arl_create("vmstat", NULL, 60);
@@ -39,7 +39,7 @@ int do_proc_vmstat(int update_every, usec_t dt) {
         arl_expect(arl_base, "pswpin", &pswpin);
         arl_expect(arl_base, "pswpout", &pswpout);
 
-        if(do_numa == CONFIG_ONDEMAND_YES || (do_numa == CONFIG_ONDEMAND_ONDEMAND && get_numa_node_count() >= 2)) {
+        if(do_numa == CONFIG_BOOLEAN_YES || (do_numa == CONFIG_BOOLEAN_AUTO && get_numa_node_count() >= 2)) {
             arl_expect(arl_base, "numa_foreign", &numa_foreign);
             arl_expect(arl_base, "numa_hint_faults_local", &numa_hint_faults_local);
             arl_expect(arl_base, "numa_hint_faults", &numa_hint_faults);
@@ -56,7 +56,7 @@ int do_proc_vmstat(int update_every, usec_t dt) {
             // when all the expected metrics are collected.
             // Also ARL will not parse their values.
             has_numa = 0;
-            do_numa = CONFIG_ONDEMAND_NO;
+            do_numa = CONFIG_BOOLEAN_NO;
         }
     }
 
@@ -87,15 +87,16 @@ int do_proc_vmstat(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(pswpin || pswpout || do_swapio == CONFIG_ONDEMAND_YES) {
-        do_swapio = CONFIG_ONDEMAND_YES;
+    if(pswpin || pswpout || do_swapio == CONFIG_BOOLEAN_YES) {
+        do_swapio = CONFIG_BOOLEAN_YES;
 
         static RRDSET *st_swapio = NULL;
         if(unlikely(!st_swapio)) {
-            st_swapio = rrdset_create("system", "swapio", NULL, "swap", NULL, "Swap I/O", "kilobytes/s", 250, update_every, RRDSET_TYPE_AREA);
+            st_swapio = rrdset_create_localhost("system", "swapio", NULL, "swap", NULL, "Swap I/O", "kilobytes/s", 250
+                                                , update_every, RRDSET_TYPE_AREA);
 
-            rrddim_add(st_swapio, "in",  NULL, sysconf(_SC_PAGESIZE), 1024, RRDDIM_INCREMENTAL);
-            rrddim_add(st_swapio, "out", NULL, -sysconf(_SC_PAGESIZE), 1024, RRDDIM_INCREMENTAL);
+            rrddim_add(st_swapio, "in",  NULL, sysconf(_SC_PAGESIZE), 1024, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st_swapio, "out", NULL, -sysconf(_SC_PAGESIZE), 1024, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st_swapio);
 
@@ -109,10 +110,11 @@ int do_proc_vmstat(int update_every, usec_t dt) {
     if(do_io) {
         static RRDSET *st_io = NULL;
         if(unlikely(!st_io)) {
-            st_io = rrdset_create("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150, update_every, RRDSET_TYPE_AREA);
+            st_io = rrdset_create_localhost("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150
+                                            , update_every, RRDSET_TYPE_AREA);
 
-            rrddim_add(st_io, "in",  NULL,  1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st_io, "out", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st_io, "in",  NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st_io, "out", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st_io);
 
@@ -126,11 +128,12 @@ int do_proc_vmstat(int update_every, usec_t dt) {
     if(do_pgfaults) {
         static RRDSET *st_pgfaults = NULL;
         if(unlikely(!st_pgfaults)) {
-            st_pgfaults = rrdset_create("mem", "pgfaults", NULL, "system", NULL, "Memory Page Faults", "page faults/s", 500, update_every, RRDSET_TYPE_LINE);
-            st_pgfaults->isdetail = 1;
+            st_pgfaults = rrdset_create_localhost("mem", "pgfaults", NULL, "system", NULL, "Memory Page Faults"
+                                                  , "page faults/s", 500, update_every, RRDSET_TYPE_LINE);
+            rrdset_flag_set(st_pgfaults, RRDSET_FLAG_DETAIL);
 
-            rrddim_add(st_pgfaults, "minor",  NULL,  1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st_pgfaults, "major", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st_pgfaults, "minor",  NULL,  1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st_pgfaults, "major", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st_pgfaults);
 
@@ -149,27 +152,28 @@ int do_proc_vmstat(int update_every, usec_t dt) {
         has_numa = (numa_local || numa_foreign || numa_interleave || numa_other || numa_pte_updates ||
                      numa_huge_pte_updates || numa_hint_faults || numa_hint_faults_local || numa_pages_migrated) ? 1 : 0;
 
-    if(do_numa == CONFIG_ONDEMAND_YES || (do_numa == CONFIG_ONDEMAND_ONDEMAND && has_numa)) {
-        do_numa = CONFIG_ONDEMAND_YES;
+    if(do_numa == CONFIG_BOOLEAN_YES || (do_numa == CONFIG_BOOLEAN_AUTO && has_numa)) {
+        do_numa = CONFIG_BOOLEAN_YES;
 
         static RRDSET *st_numa = NULL;
         if(unlikely(!st_numa)) {
-            st_numa = rrdset_create("mem", "numa", NULL, "numa", NULL, "NUMA events", "events/s", 800, update_every, RRDSET_TYPE_LINE);
-            st_numa->isdetail = 1;
+            st_numa = rrdset_create_localhost("mem", "numa", NULL, "numa", NULL, "NUMA events", "events/s", 800
+                                              , update_every, RRDSET_TYPE_LINE);
+            rrdset_flag_set(st_numa, RRDSET_FLAG_DETAIL);
 
             // These depend on CONFIG_NUMA in the kernel.
-            rrddim_add(st_numa, "local", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st_numa, "foreign", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st_numa, "interleave", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st_numa, "other", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st_numa, "local", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st_numa, "foreign", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st_numa, "interleave", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st_numa, "other", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
 
             // The following stats depend on CONFIG_NUMA_BALANCING in the
             // kernel.
-            rrddim_add(st_numa, "pte updates", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st_numa, "huge pte updates", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st_numa, "hint faults", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st_numa, "hint faults local", NULL, 1, 1, RRDDIM_INCREMENTAL);
-            rrddim_add(st_numa, "pages migrated", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st_numa, "pte updates", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st_numa, "huge pte updates", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st_numa, "hint faults", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st_numa, "hint faults local", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+            rrddim_add(st_numa, "pages migrated", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else rrdset_next(st_numa);
 
index dc4a5409b46a54a6efeaa09d23f681118866a30c..3a89e83539c75371db0f7c8c77565463d746a772 100644 (file)
@@ -128,6 +128,8 @@ static inline void pflines_free(pflines *fl) {
 // The procfile
 
 void procfile_close(procfile *ff) {
+    if(unlikely(!ff)) return;
+
     debug(D_PROCFILE, PF_PREFIX ": Closing file '%s'", procfile_filename(ff));
 
     if(likely(ff->lines)) pflines_free(ff->lines);
index d223cd6f1e5076efe4714b32deff61ab4820c35d..ec6fb83bad37309d188d30d830e6daa98e1f0f51 100644 (file)
@@ -41,19 +41,19 @@ static inline void registry_set_person_cookie(struct web_client *w, REGISTRY_PER
 // ----------------------------------------------------------------------------
 // JSON GENERATION
 
-static inline void registry_json_header(struct web_client *w, const char *action, const char *status) {
+static inline void registry_json_header(RRDHOST *host, struct web_client *w, const char *action, const char *status) {
     buffer_flush(w->response.data);
     w->response.data->contenttype = CT_APPLICATION_JSON;
     buffer_sprintf(w->response.data, "{\n\t\"action\": \"%s\",\n\t\"status\": \"%s\",\n\t\"hostname\": \"%s\",\n\t\"machine_guid\": \"%s\"",
-            action, status, registry.hostname, registry.machine_guid);
+            action, status, (host == localhost)?registry.hostname:host->hostname, host->machine_guid);
 }
 
 static inline void registry_json_footer(struct web_client *w) {
     buffer_strcat(w->response.data, "\n}\n");
 }
 
-static inline int registry_json_disabled(struct web_client *w, const char *action) {
-    registry_json_header(w, action, REGISTRY_STATUS_DISABLED);
+static inline int registry_json_disabled(RRDHOST *host, struct web_client *w, const char *action) {
+    registry_json_header(host, w, action, REGISTRY_STATUS_DISABLED);
 
     buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"",
             registry.registry_to_announce);
@@ -127,8 +127,8 @@ static inline int registry_person_url_callback_verify_machine_exists(void *entry
 // ----------------------------------------------------------------------------
 // public HELLO request
 
-int registry_request_hello_json(struct web_client *w) {
-    registry_json_header(w, "hello", REGISTRY_STATUS_OK);
+int registry_request_hello_json(RRDHOST *host, struct web_client *w) {
+    registry_json_header(host, w, "hello", REGISTRY_STATUS_OK);
 
     buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"",
             registry.registry_to_announce);
@@ -143,9 +143,9 @@ int registry_request_hello_json(struct web_client *w) {
 #define REGISTRY_VERIFY_COOKIES_GUID "give-me-back-this-cookie-now--please"
 
 // the main method for registering an access
-int registry_request_access_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when) {
+int registry_request_access_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when) {
     if(unlikely(!registry.enabled))
-        return registry_json_disabled(w, "access");
+        return registry_json_disabled(host, w, "access");
 
     // ------------------------------------------------------------------------
     // verify the browser supports cookies
@@ -167,7 +167,7 @@ int registry_request_access_json(struct web_client *w, char *person_guid, char *
 
     REGISTRY_PERSON *p = registry_request_access(person_guid, machine_guid, url, name, when);
     if(!p) {
-        registry_json_header(w, "access", REGISTRY_STATUS_FAILED);
+        registry_json_header(host, w, "access", REGISTRY_STATUS_FAILED);
         registry_json_footer(w);
         registry_unlock();
         return 412;
@@ -177,7 +177,7 @@ int registry_request_access_json(struct web_client *w, char *person_guid, char *
     registry_set_person_cookie(w, p);
 
     // generate the response
-    registry_json_header(w, "access", REGISTRY_STATUS_OK);
+    registry_json_header(host, w, "access", REGISTRY_STATUS_OK);
 
     buffer_sprintf(w->response.data, ",\n\t\"person_guid\": \"%s\",\n\t\"urls\": [", p->guid);
     struct registry_json_walk_person_urls_callback c = { p, NULL, w, 0 };
@@ -193,22 +193,22 @@ int registry_request_access_json(struct web_client *w, char *person_guid, char *
 // public DELETE request
 
 // the main method for deleting a URL from a person
-int registry_request_delete_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when) {
+int registry_request_delete_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when) {
     if(!registry.enabled)
-        return registry_json_disabled(w, "delete");
+        return registry_json_disabled(host, w, "delete");
 
     registry_lock();
 
     REGISTRY_PERSON *p = registry_request_delete(person_guid, machine_guid, url, delete_url, when);
     if(!p) {
-        registry_json_header(w, "delete", REGISTRY_STATUS_FAILED);
+        registry_json_header(host, w, "delete", REGISTRY_STATUS_FAILED);
         registry_json_footer(w);
         registry_unlock();
         return 412;
     }
 
     // generate the response
-    registry_json_header(w, "delete", REGISTRY_STATUS_OK);
+    registry_json_header(host, w, "delete", REGISTRY_STATUS_OK);
     registry_json_footer(w);
     registry_unlock();
     return 200;
@@ -218,21 +218,21 @@ int registry_request_delete_json(struct web_client *w, char *person_guid, char *
 // public SEARCH request
 
 // the main method for searching the URLs of a netdata
-int registry_request_search_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when) {
+int registry_request_search_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when) {
     if(!registry.enabled)
-        return registry_json_disabled(w, "search");
+        return registry_json_disabled(host, w, "search");
 
     registry_lock();
 
     REGISTRY_MACHINE *m = registry_request_machine(person_guid, machine_guid, url, request_machine, when);
     if(!m) {
-        registry_json_header(w, "search", REGISTRY_STATUS_FAILED);
+        registry_json_header(host, w, "search", REGISTRY_STATUS_FAILED);
         registry_json_footer(w);
         registry_unlock();
         return 404;
     }
 
-    registry_json_header(w, "search", REGISTRY_STATUS_OK);
+    registry_json_header(host, w, "search", REGISTRY_STATUS_OK);
 
     buffer_strcat(w->response.data, ",\n\t\"urls\": [");
     struct registry_json_walk_person_urls_callback c = { NULL, m, w, 0 };
@@ -248,9 +248,9 @@ int registry_request_search_json(struct web_client *w, char *person_guid, char *
 // SWITCH REQUEST
 
 // the main method for switching user identity
-int registry_request_switch_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *new_person_guid, time_t when) {
+int registry_request_switch_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *new_person_guid, time_t when) {
     if(!registry.enabled)
-        return registry_json_disabled(w, "switch");
+        return registry_json_disabled(host, w, "switch");
 
     (void)url;
     (void)when;
@@ -259,7 +259,7 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char *
 
     REGISTRY_PERSON *op = registry_person_find(person_guid);
     if(!op) {
-        registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
+        registry_json_header(host, w, "switch", REGISTRY_STATUS_FAILED);
         registry_json_footer(w);
         registry_unlock();
         return 430;
@@ -267,7 +267,7 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char *
 
     REGISTRY_PERSON *np = registry_person_find(new_person_guid);
     if(!np) {
-        registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
+        registry_json_header(host, w, "switch", REGISTRY_STATUS_FAILED);
         registry_json_footer(w);
         registry_unlock();
         return 431;
@@ -275,7 +275,7 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char *
 
     REGISTRY_MACHINE *m = registry_machine_find(machine_guid);
     if(!m) {
-        registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
+        registry_json_header(host, w, "switch", REGISTRY_STATUS_FAILED);
         registry_json_footer(w);
         registry_unlock();
         return 432;
@@ -286,7 +286,7 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char *
     // verify the old person has access to this machine
     avl_traverse(&op->person_urls, registry_person_url_callback_verify_machine_exists, &data);
     if(!data.count) {
-        registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
+        registry_json_header(host, w, "switch", REGISTRY_STATUS_FAILED);
         registry_json_footer(w);
         registry_unlock();
         return 433;
@@ -296,7 +296,7 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char *
     data.count = 0;
     avl_traverse(&np->person_urls, registry_person_url_callback_verify_machine_exists, &data);
     if(!data.count) {
-        registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
+        registry_json_header(host, w, "switch", REGISTRY_STATUS_FAILED);
         registry_json_footer(w);
         registry_unlock();
         return 434;
@@ -307,7 +307,7 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char *
     registry_set_person_cookie(w, np);
 
     // generate the response
-    registry_json_header(w, "switch", REGISTRY_STATUS_OK);
+    registry_json_header(host, w, "switch", REGISTRY_STATUS_OK);
     buffer_sprintf(w->response.data, ",\n\t\"person_guid\": \"%s\"", np->guid);
     registry_json_footer(w);
 
@@ -323,11 +323,13 @@ void registry_statistics(void) {
 
     static RRDSET *sts = NULL, *stc = NULL, *stm = NULL;
 
-    if(!sts) sts = rrdset_find("netdata.registry_sessions");
+    if(!sts) sts = rrdset_find_localhost("netdata.registry_sessions");
     if(!sts) {
-        sts = rrdset_create("netdata", "registry_sessions", NULL, "registry", NULL, "NetData Registry Sessions", "session", 131000, rrd_update_every, RRDSET_TYPE_LINE);
+        sts = rrdset_create_localhost("netdata", "registry_sessions", NULL, "registry", NULL
+                                      , "NetData Registry Sessions", "session", 131000, localhost->rrd_update_every
+                                      , RRDSET_TYPE_LINE);
 
-        rrddim_add(sts, "sessions",  NULL,  1, 1, RRDDIM_ABSOLUTE);
+        rrddim_add(sts, "sessions",  NULL,  1, 1, RRD_ALGORITHM_ABSOLUTE);
     }
     else rrdset_next(sts);
 
@@ -336,15 +338,16 @@ void registry_statistics(void) {
 
     // ------------------------------------------------------------------------
 
-    if(!stc) stc = rrdset_find("netdata.registry_entries");
+    if(!stc) stc = rrdset_find_localhost("netdata.registry_entries");
     if(!stc) {
-        stc = rrdset_create("netdata", "registry_entries", NULL, "registry", NULL, "NetData Registry Entries", "entries", 131100, rrd_update_every, RRDSET_TYPE_LINE);
-
-        rrddim_add(stc, "persons",        NULL,  1, 1, RRDDIM_ABSOLUTE);
-        rrddim_add(stc, "machines",       NULL,  1, 1, RRDDIM_ABSOLUTE);
-        rrddim_add(stc, "urls",           NULL,  1, 1, RRDDIM_ABSOLUTE);
-        rrddim_add(stc, "persons_urls",   NULL,  1, 1, RRDDIM_ABSOLUTE);
-        rrddim_add(stc, "machines_urls",  NULL,  1, 1, RRDDIM_ABSOLUTE);
+        stc = rrdset_create_localhost("netdata", "registry_entries", NULL, "registry", NULL, "NetData Registry Entries"
+                                      , "entries", 131100, localhost->rrd_update_every, RRDSET_TYPE_LINE);
+
+        rrddim_add(stc, "persons",        NULL,  1, 1, RRD_ALGORITHM_ABSOLUTE);
+        rrddim_add(stc, "machines",       NULL,  1, 1, RRD_ALGORITHM_ABSOLUTE);
+        rrddim_add(stc, "urls",           NULL,  1, 1, RRD_ALGORITHM_ABSOLUTE);
+        rrddim_add(stc, "persons_urls",   NULL,  1, 1, RRD_ALGORITHM_ABSOLUTE);
+        rrddim_add(stc, "machines_urls",  NULL,  1, 1, RRD_ALGORITHM_ABSOLUTE);
     }
     else rrdset_next(stc);
 
@@ -357,15 +360,16 @@ void registry_statistics(void) {
 
     // ------------------------------------------------------------------------
 
-    if(!stm) stm = rrdset_find("netdata.registry_mem");
+    if(!stm) stm = rrdset_find_localhost("netdata.registry_mem");
     if(!stm) {
-        stm = rrdset_create("netdata", "registry_mem", NULL, "registry", NULL, "NetData Registry Memory", "KB", 131300, rrd_update_every, RRDSET_TYPE_STACKED);
-
-        rrddim_add(stm, "persons",        NULL,  1, 1024, RRDDIM_ABSOLUTE);
-        rrddim_add(stm, "machines",       NULL,  1, 1024, RRDDIM_ABSOLUTE);
-        rrddim_add(stm, "urls",           NULL,  1, 1024, RRDDIM_ABSOLUTE);
-        rrddim_add(stm, "persons_urls",   NULL,  1, 1024, RRDDIM_ABSOLUTE);
-        rrddim_add(stm, "machines_urls",  NULL,  1, 1024, RRDDIM_ABSOLUTE);
+        stm = rrdset_create_localhost("netdata", "registry_mem", NULL, "registry", NULL, "NetData Registry Memory", "KB"
+                                      , 131300, localhost->rrd_update_every, RRDSET_TYPE_STACKED);
+
+        rrddim_add(stm, "persons",        NULL,  1, 1024, RRD_ALGORITHM_ABSOLUTE);
+        rrddim_add(stm, "machines",       NULL,  1, 1024, RRD_ALGORITHM_ABSOLUTE);
+        rrddim_add(stm, "urls",           NULL,  1, 1024, RRD_ALGORITHM_ABSOLUTE);
+        rrddim_add(stm, "persons_urls",   NULL,  1, 1024, RRD_ALGORITHM_ABSOLUTE);
+        rrddim_add(stm, "machines_urls",  NULL,  1, 1024, RRD_ALGORITHM_ABSOLUTE);
     }
     else rrdset_next(stm);
 
index 4947486c4351bc5ae6088fcd76ac97287cafdf1b..2c4592b922e20e5abe8423fa06abdde14489ab0f 100644 (file)
@@ -60,13 +60,16 @@ extern int registry_init(void);
 extern void registry_free(void);
 
 // HTTP requests handled by the registry
-extern int registry_request_access_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when);
-extern int registry_request_delete_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when);
-extern int registry_request_search_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when);
-extern int registry_request_switch_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *new_person_guid, time_t when);
-extern int registry_request_hello_json(struct web_client *w);
+extern int registry_request_access_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when);
+extern int registry_request_delete_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when);
+extern int registry_request_search_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when);
+extern int registry_request_switch_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *new_person_guid, time_t when);
+extern int registry_request_hello_json(RRDHOST *host, struct web_client *w);
 
 // update the registry monitoring charts
 extern void registry_statistics(void);
 
+extern char *registry_get_this_machine_guid(void);
+extern int regenerate_guid(const char *guid, char *result);
+
 #endif /* NETDATA_REGISTRY_H */
index a846f861174ba2059365357517b1f4ba7d1c949f..0916726046415e280ac1bc3485a9f445c0464615 100644 (file)
@@ -4,46 +4,52 @@ int registry_init(void) {
     char filename[FILENAME_MAX + 1];
 
     // registry enabled?
-    registry.enabled = config_get_boolean("registry", "enabled", 0);
+    if(web_server_mode != WEB_SERVER_MODE_NONE) {
+        registry.enabled = config_get_boolean(CONFIG_SECTION_REGISTRY, "enabled", 0);
+    }
+    else {
+        info("Registry is disabled - use the central netdata");
+        config_set_boolean(CONFIG_SECTION_REGISTRY, "enabled", 0);
+        registry.enabled = 0;
+    }
 
     // pathnames
     snprintfz(filename, FILENAME_MAX, "%s/registry", netdata_configured_varlib_dir);
-    registry.pathname = config_get("registry", "registry db directory", filename);
+    registry.pathname = config_get(CONFIG_SECTION_REGISTRY, "registry db directory", filename);
     if(mkdir(registry.pathname, 0770) == -1 && errno != EEXIST)
         fatal("Cannot create directory '%s'.", registry.pathname);
 
     // filenames
     snprintfz(filename, FILENAME_MAX, "%s/netdata.public.unique.id", registry.pathname);
-    registry.machine_guid_filename = config_get("registry", "netdata unique id file", filename);
-    registry_get_this_machine_guid();
+    registry.machine_guid_filename = config_get(CONFIG_SECTION_REGISTRY, "netdata unique id file", filename);
 
     snprintfz(filename, FILENAME_MAX, "%s/registry.db", registry.pathname);
-    registry.db_filename = config_get("registry", "registry db file", filename);
+    registry.db_filename = config_get(CONFIG_SECTION_REGISTRY, "registry db file", filename);
 
     snprintfz(filename, FILENAME_MAX, "%s/registry-log.db", registry.pathname);
-    registry.log_filename = config_get("registry", "registry log file", filename);
+    registry.log_filename = config_get(CONFIG_SECTION_REGISTRY, "registry log file", filename);
 
     // configuration options
-    registry.save_registry_every_entries = (unsigned long long)config_get_number("registry", "registry save db every new entries", 1000000);
-    registry.persons_expiration = config_get_number("registry", "registry expire idle persons days", 365) * 86400;
-    registry.registry_domain = config_get("registry", "registry domain", "");
-    registry.registry_to_announce = config_get("registry", "registry to announce", "https://registry.my-netdata.io");
-    registry.hostname = config_get("registry", "registry hostname", config_get("global", "hostname", localhost.hostname));
-    registry.verify_cookies_redirects = config_get_boolean("registry", "verify browser cookies support", 1);
+    registry.save_registry_every_entries = (unsigned long long)config_get_number(CONFIG_SECTION_REGISTRY, "registry save db every new entries", 1000000);
+    registry.persons_expiration = config_get_number(CONFIG_SECTION_REGISTRY, "registry expire idle persons days", 365) * 86400;
+    registry.registry_domain = config_get(CONFIG_SECTION_REGISTRY, "registry domain", "");
+    registry.registry_to_announce = config_get(CONFIG_SECTION_REGISTRY, "registry to announce", "https://registry.my-netdata.io");
+    registry.hostname = config_get(CONFIG_SECTION_REGISTRY, "registry hostname", config_get(CONFIG_SECTION_GLOBAL, "hostname", "localhost"));
+    registry.verify_cookies_redirects = config_get_boolean(CONFIG_SECTION_REGISTRY, "verify browser cookies support", 1);
 
     setenv("NETDATA_REGISTRY_HOSTNAME", registry.hostname, 1);
     setenv("NETDATA_REGISTRY_URL", registry.registry_to_announce, 1);
 
-    registry.max_url_length = (size_t)config_get_number("registry", "max URL length", 1024);
+    registry.max_url_length = (size_t)config_get_number(CONFIG_SECTION_REGISTRY, "max URL length", 1024);
     if(registry.max_url_length < 10) {
         registry.max_url_length = 10;
-        config_set_number("registry", "max URL length", (long long)registry.max_url_length);
+        config_set_number(CONFIG_SECTION_REGISTRY, "max URL length", (long long)registry.max_url_length);
     }
 
-    registry.max_name_length = (size_t)config_get_number("registry", "max URL name length", 50);
+    registry.max_name_length = (size_t)config_get_number(CONFIG_SECTION_REGISTRY, "max URL name length", 50);
     if(registry.max_name_length < 10) {
         registry.max_name_length = 10;
-        config_set_number("registry", "max URL name length", (long long)registry.max_name_length);
+        config_set_number(CONFIG_SECTION_REGISTRY, "max URL name length", (long long)registry.max_name_length);
     }
 
     // initialize entries counters
index d32d549e28352e9a87365068d6fc173e6945b4ad..9ec91ba401eb2a673421411545817f7651251d10 100644 (file)
@@ -7,7 +7,7 @@ struct registry registry;
 
 // parse a GUID and re-generated to be always lower case
 // this is used as a protection against the variations of GUIDs
-int registry_regenerate_guid(const char *guid, char *result) {
+int regenerate_guid(const char *guid, char *result) {
     uuid_t uuid;
     if(unlikely(uuid_parse(guid, uuid) == -1)) {
         info("Registry: GUID '%s' is not a valid GUID.", guid);
@@ -18,7 +18,7 @@ int registry_regenerate_guid(const char *guid, char *result) {
 
 #ifdef NETDATA_INTERNAL_CHECKS
         if(strcmp(guid, result))
-            info("Registry: source GUID '%s' and re-generated GUID '%s' differ!", guid, result);
+            info("GUID '%s' and re-generated GUID '%s' differ!", guid, result);
 #endif /* NETDATA_INTERNAL_CHECKS */
     }
 
@@ -96,14 +96,14 @@ REGISTRY_PERSON_URL *registry_verify_request(char *person_guid, char *machine_gu
     url = registry_fix_url(url, NULL);
 
     // make sure the person GUID is valid
-    if(registry_regenerate_guid(person_guid, pbuf) == -1) {
+    if(regenerate_guid(person_guid, pbuf) == -1) {
         info("Registry Request Verification: invalid person GUID, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
         return NULL;
     }
     person_guid = pbuf;
 
     // make sure the machine GUID is valid
-    if(registry_regenerate_guid(machine_guid, mbuf) == -1) {
+    if(regenerate_guid(machine_guid, mbuf) == -1) {
         info("Registry Request Verification: invalid machine GUID, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
         return NULL;
     }
@@ -226,7 +226,7 @@ REGISTRY_MACHINE *registry_request_machine(char *person_guid, char *machine_guid
     if(!pu || !p || !m) return NULL;
 
     // make sure the machine GUID is valid
-    if(registry_regenerate_guid(request_machine, mbuf) == -1) {
+    if(regenerate_guid(request_machine, mbuf) == -1) {
         info("Registry Machine URLs request: invalid machine GUID, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, m->guid, pu->url->url, request_machine);
         return NULL;
     }
@@ -275,8 +275,10 @@ static inline int is_machine_guid_blacklisted(const char *guid) {
 }
 
 char *registry_get_this_machine_guid(void) {
-    if(likely(registry.machine_guid[0]))
-        return registry.machine_guid;
+    static char guid[GUID_LEN + 1] = "";
+
+    if(likely(guid[0]))
+        return guid;
 
     // read it from disk
     int fd = open(registry.machine_guid_filename, O_RDONLY);
@@ -286,38 +288,38 @@ char *registry_get_this_machine_guid(void) {
             error("Failed to read machine GUID from '%s'", registry.machine_guid_filename);
         else {
             buf[GUID_LEN] = '\0';
-            if(registry_regenerate_guid(buf, registry.machine_guid) == -1) {
+            if(regenerate_guid(buf, guid) == -1) {
                 error("Failed to validate machine GUID '%s' from '%s'. Ignoring it - this might mean this netdata will appear as duplicate in the registry.",
                         buf, registry.machine_guid_filename);
 
-                registry.machine_guid[0] = '\0';
+                guid[0] = '\0';
             }
-            else if(is_machine_guid_blacklisted(registry.machine_guid))
-                registry.machine_guid[0] = '\0';
+            else if(is_machine_guid_blacklisted(guid))
+                guid[0] = '\0';
         }
         close(fd);
     }
 
     // generate a new one?
-    if(!registry.machine_guid[0]) {
+    if(!guid[0]) {
         uuid_t uuid;
 
         uuid_generate_time(uuid);
-        uuid_unparse_lower(uuid, registry.machine_guid);
-        registry.machine_guid[GUID_LEN] = '\0';
+        uuid_unparse_lower(uuid, guid);
+        guid[GUID_LEN] = '\0';
 
         // save it
         fd = open(registry.machine_guid_filename, O_WRONLY|O_CREAT|O_TRUNC, 444);
         if(fd == -1)
             fatal("Cannot create unique machine id file '%s'. Please fix this.", registry.machine_guid_filename);
 
-        if(write(fd, registry.machine_guid, GUID_LEN) != GUID_LEN)
+        if(write(fd, guid, GUID_LEN) != GUID_LEN)
             fatal("Cannot write the unique machine id file '%s'. Please fix this.", registry.machine_guid_filename);
 
         close(fd);
     }
 
-    setenv("NETDATA_REGISTRY_UNIQUE_ID", registry.machine_guid, 1);
+    setenv("NETDATA_REGISTRY_UNIQUE_ID", guid, 1);
 
-    return registry.machine_guid;
+    return guid;
 }
index 9c0b744524c7eeb0364c4271c0b3ce1e61112408..27c2fe0745de4e1b5d09680eae75a4eea058b919 100644 (file)
@@ -14,8 +14,6 @@
 struct registry {
     int enabled;
 
-    char machine_guid[GUID_LEN + 1];
-
     // entries counters / statistics
     unsigned long long persons_count;
     unsigned long long machines_count;
@@ -61,7 +59,7 @@ struct registry {
     pthread_mutex_t lock;
 };
 
-extern int registry_regenerate_guid(const char *guid, char *result);
+extern int regenerate_guid(const char *guid, char *result);
 
 #include "registry_url.h"
 #include "registry_machine.h"
@@ -70,8 +68,6 @@ extern int registry_regenerate_guid(const char *guid, char *result);
 
 extern struct registry registry;
 
-extern char *registry_get_this_machine_guid(void);
-
 // REGISTRY LOW-LEVEL REQUESTS (in registry-internals.c)
 extern REGISTRY_PERSON *registry_request_access(char *person_guid, char *machine_guid, char *url, char *name, time_t when);
 extern REGISTRY_PERSON *registry_request_delete(char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when);
index 3510736df4de73ed35ea247c024e194d4e677c14..6dc8200d39522f83c94e7d58fff2ea960b908949 100644 (file)
@@ -58,7 +58,7 @@ REGISTRY_MACHINE *registry_machine_get(const char *machine_guid, time_t when) {
     if(likely(machine_guid && *machine_guid)) {
         // validate it is a GUID
         char buf[GUID_LEN + 1];
-        if(unlikely(registry_regenerate_guid(machine_guid, buf) == -1))
+        if(unlikely(regenerate_guid(machine_guid, buf) == -1))
             info("Registry: machine guid '%s' is not a valid guid. Ignoring it.", machine_guid);
         else {
             machine_guid = buf;
index 5f9099c9aa6995eb2bc761910c31865a680393d9..409c76925ba54719cff0fc85c4e75c0753331cbb 100644 (file)
@@ -183,7 +183,7 @@ REGISTRY_PERSON *registry_person_get(const char *person_guid, time_t when) {
     if(person_guid && *person_guid) {
         char buf[GUID_LEN + 1];
         // validate it is a GUID
-        if(unlikely(registry_regenerate_guid(person_guid, buf) == -1))
+        if(unlikely(regenerate_guid(person_guid, buf) == -1))
             info("Registry: person guid '%s' is not a valid guid. Ignoring it.", person_guid);
         else {
             person_guid = buf;
index a17704394165246919c0c7b4783198a5b7487256..a9ff6243db06989c458a9ecf7f0b5480006d4f60 100644 (file)
--- a/src/rrd.c
+++ b/src/rrd.c
@@ -1,7 +1,6 @@
+#define NETDATA_RRD_INTERNALS 1
 #include "common.h"
 
-#define RRD_DEFAULT_GAP_INTERPOLATIONS 1
-
 // ----------------------------------------------------------------------------
 // globals
 
 int rrd_delete_unupdated_dimensions = 0;
 */
 
-int rrd_update_every = UPDATE_EVERY;
-int rrd_default_history_entries = RRD_DEFAULT_HISTORY_ENTRIES;
-int rrd_memory_mode = RRD_MEMORY_MODE_SAVE;
+int default_rrd_update_every = UPDATE_EVERY;
+int default_rrd_history_entries = RRD_DEFAULT_HISTORY_ENTRIES;
+RRD_MEMORY_MODE default_rrd_memory_mode = RRD_MEMORY_MODE_SAVE;
 
-static int rrdset_compare(void* a, void* b);
-static int rrdset_compare_name(void* a, void* b);
-static int rrdfamily_compare(void *a, void *b);
 
 // ----------------------------------------------------------------------------
-// RRDHOST
-
-RRDHOST localhost = {
-        .hostname = "localhost",
-        .rrdset_root = NULL,
-        .rrdset_root_rwlock = PTHREAD_RWLOCK_INITIALIZER,
-        .rrdset_root_index = {
-            { NULL, rrdset_compare },
-            AVL_LOCK_INITIALIZER
-        },
-        .rrdset_root_index_name = {
-            { NULL, rrdset_compare_name },
-            AVL_LOCK_INITIALIZER
-        },
-        .rrdfamily_root_index = {
-            { NULL, rrdfamily_compare },
-            AVL_LOCK_INITIALIZER
-        },
-        .variables_root_index = {
-            { NULL, rrdvar_compare },
-            AVL_LOCK_INITIALIZER
-        },
-        .health_log = {
-            .next_log_id = 1,
-            .next_alarm_id = 1,
-            .count = 0,
-            .max = 1000,
-            .alarms = NULL,
-            .alarm_log_rwlock = PTHREAD_RWLOCK_INITIALIZER
-        }
-};
-
-void rrdhost_init(char *hostname) {
-    localhost.hostname = hostname;
-    localhost.health_log.next_log_id =
-        localhost.health_log.next_alarm_id = now_realtime_sec();
-}
-
-void rrdhost_rwlock(RRDHOST *host) {
-    pthread_rwlock_wrlock(&host->rrdset_root_rwlock);
-}
-
-void rrdhost_rdlock(RRDHOST *host) {
-    pthread_rwlock_rdlock(&host->rrdset_root_rwlock);
-}
-
-void rrdhost_unlock(RRDHOST *host) {
-    pthread_rwlock_unlock(&host->rrdset_root_rwlock);
-}
-
-void rrdhost_check_rdlock_int(RRDHOST *host, const char *file, const char *function, const unsigned long line) {
-    int ret = pthread_rwlock_trywrlock(&host->rrdset_root_rwlock);
-
-    if(ret == 0)
-        fatal("RRDHOST '%s' should be read-locked, but it is not, at function %s() at line %lu of file '%s'", host->hostname, function, line, file);
-}
-
-void rrdhost_check_wrlock_int(RRDHOST *host, const char *file, const char *function, const unsigned long line) {
-    int ret = pthread_rwlock_tryrdlock(&host->rrdset_root_rwlock);
-
-    if(ret == 0)
-        fatal("RRDHOST '%s' should be write-locked, but it is not, at function %s() at line %lu of file '%s'", host->hostname, function, line, file);
-}
-
-// ----------------------------------------------------------------------------
-// RRDFAMILY index
-
-static int rrdfamily_compare(void *a, void *b) {
-    if(((RRDFAMILY *)a)->hash_family < ((RRDFAMILY *)b)->hash_family) return -1;
-    else if(((RRDFAMILY *)a)->hash_family > ((RRDFAMILY *)b)->hash_family) return 1;
-    else return strcmp(((RRDFAMILY *)a)->family, ((RRDFAMILY *)b)->family);
-}
-
-#define rrdfamily_index_add(host, rc) (RRDFAMILY *)avl_insert_lock(&((host)->rrdfamily_root_index), (avl *)(rc))
-#define rrdfamily_index_del(host, rc) (RRDFAMILY *)avl_remove_lock(&((host)->rrdfamily_root_index), (avl *)(rc))
-
-static RRDFAMILY *rrdfamily_index_find(RRDHOST *host, const char *id, uint32_t hash) {
-    RRDFAMILY tmp;
-    tmp.family = id;
-    tmp.hash_family = (hash)?hash:simple_hash(tmp.family);
-
-    return (RRDFAMILY *)avl_search_lock(&(host->rrdfamily_root_index), (avl *) &tmp);
-}
-
-RRDFAMILY *rrdfamily_create(const char *id) {
-    RRDFAMILY *rc = rrdfamily_index_find(&localhost, id, 0);
-    if(!rc) {
-        rc = callocz(1, sizeof(RRDFAMILY));
+// RRD - memory modes
 
-        rc->family = strdupz(id);
-        rc->hash_family = simple_hash(rc->family);
-
-        // initialize the variables index
-        avl_init_lock(&rc->variables_root_index, rrdvar_compare);
-
-        RRDFAMILY *ret = rrdfamily_index_add(&localhost, rc);
-        if(ret != rc)
-            fatal("RRDFAMILY: INTERNAL ERROR: Expected to INSERT RRDFAMILY '%s' into index, but inserted '%s'.", rc->family, (ret)?ret->family:"NONE");
-    }
-
-    rc->use_count++;
-    return rc;
-}
+inline const char *rrd_memory_mode_name(RRD_MEMORY_MODE id) {
+    switch(id) {
+        case RRD_MEMORY_MODE_RAM:
+            return RRD_MEMORY_MODE_RAM_NAME;
 
-void rrdfamily_free(RRDFAMILY *rc) {
-    rc->use_count--;
-    if(!rc->use_count) {
-        RRDFAMILY *ret = rrdfamily_index_del(&localhost, rc);
-        if(ret != rc)
-            fatal("RRDFAMILY: INTERNAL ERROR: Expected to DELETE RRDFAMILY '%s' from index, but deleted '%s'.", rc->family, (ret)?ret->family:"NONE");
+        case RRD_MEMORY_MODE_MAP:
+            return RRD_MEMORY_MODE_MAP_NAME;
 
-        if(rc->variables_root_index.avl_tree.root != NULL)
-            fatal("RRDFAMILY: INTERNAL ERROR: Variables index of RRDFAMILY '%s' that is freed, is not empty.", rc->family);
+        case RRD_MEMORY_MODE_NONE:
+            return RRD_MEMORY_MODE_NONE_NAME;
 
-        freez((void *)rc->family);
-        freez(rc);
+        case RRD_MEMORY_MODE_SAVE:
+        default:
+            return RRD_MEMORY_MODE_SAVE_NAME;
     }
 }
 
-// ----------------------------------------------------------------------------
-// RRDSET index
+RRD_MEMORY_MODE rrd_memory_mode_id(const char *name) {
+    if(unlikely(!strcmp(name, RRD_MEMORY_MODE_RAM_NAME)))
+        return RRD_MEMORY_MODE_RAM;
+    else if(unlikely(!strcmp(name, RRD_MEMORY_MODE_MAP_NAME)))
+        return RRD_MEMORY_MODE_MAP;
+    else if(unlikely(!strcmp(name, RRD_MEMORY_MODE_NONE_NAME)))
+        return RRD_MEMORY_MODE_NONE;
 
-static int rrdset_compare(void* a, void* b) {
-    if(((RRDSET *)a)->hash < ((RRDSET *)b)->hash) return -1;
-    else if(((RRDSET *)a)->hash > ((RRDSET *)b)->hash) return 1;
-    else return strcmp(((RRDSET *)a)->id, ((RRDSET *)b)->id);
+    return RRD_MEMORY_MODE_SAVE;
 }
 
-#define rrdset_index_add(host, st) (RRDSET *)avl_insert_lock(&((host)->rrdset_root_index), (avl *)(st))
-#define rrdset_index_del(host, st) (RRDSET *)avl_remove_lock(&((host)->rrdset_root_index), (avl *)(st))
-
-static RRDSET *rrdset_index_find(RRDHOST *host, const char *id, uint32_t hash) {
-    RRDSET tmp;
-    strncpyz(tmp.id, id, RRD_ID_LENGTH_MAX);
-    tmp.hash = (hash)?hash:simple_hash(tmp.id);
-
-    return (RRDSET *)avl_search_lock(&(host->rrdset_root_index), (avl *) &tmp);
-}
 
 // ----------------------------------------------------------------------------
-// RRDSET name index
+// RRD - algorithms types
 
-#define rrdset_from_avlname(avlname_ptr) ((RRDSET *)((avlname_ptr) - offsetof(RRDSET, avlname)))
+RRD_ALGORITHM rrd_algorithm_id(const char *name) {
+    if(strcmp(name, RRD_ALGORITHM_INCREMENTAL_NAME) == 0)
+        return RRD_ALGORITHM_INCREMENTAL;
 
-static int rrdset_compare_name(void* a, void* b) {
-    RRDSET *A = rrdset_from_avlname(a);
-    RRDSET *B = rrdset_from_avlname(b);
+    else if(strcmp(name, RRD_ALGORITHM_ABSOLUTE_NAME) == 0)
+        return RRD_ALGORITHM_ABSOLUTE;
 
-    // fprintf(stderr, "COMPARING: %s with %s\n", A->name, B->name);
+    else if(strcmp(name, RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL_NAME) == 0)
+        return RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL;
 
-    if(A->hash_name < B->hash_name) return -1;
-    else if(A->hash_name > B->hash_name) return 1;
-    else return strcmp(A->name, B->name);
-}
+    else if(strcmp(name, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL_NAME) == 0)
+        return RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL;
 
-RRDSET *rrdset_index_add_name(RRDHOST *host, RRDSET *st) {
-    void *result;
-    // fprintf(stderr, "ADDING: %s (name: %s)\n", st->id, st->name);
-    result = avl_insert_lock(&host->rrdset_root_index_name, (avl *) (&st->avlname));
-    if(result) return rrdset_from_avlname(result);
-    return NULL;
+    else
+        return RRD_ALGORITHM_ABSOLUTE;
 }
 
-RRDSET *rrdset_index_del_name(RRDHOST *host, RRDSET *st) {
-    void *result;
-    // fprintf(stderr, "DELETING: %s (name: %s)\n", st->id, st->name);
-    result = (RRDSET *)avl_remove_lock(&((host)->rrdset_root_index_name), (avl *)(&st->avlname));
-    if(result) return rrdset_from_avlname(result);
-    return NULL;
-}
+const char *rrd_algorithm_name(RRD_ALGORITHM algorithm) {
+    switch(algorithm) {
+        case RRD_ALGORITHM_ABSOLUTE:
+        default:
+            return RRD_ALGORITHM_ABSOLUTE_NAME;
 
-static RRDSET *rrdset_index_find_name(RRDHOST *host, const char *name, uint32_t hash) {
-    void *result = NULL;
-    RRDSET tmp;
-    tmp.name = name;
-    tmp.hash_name = (hash)?hash:simple_hash(tmp.name);
+        case RRD_ALGORITHM_INCREMENTAL:
+            return RRD_ALGORITHM_INCREMENTAL_NAME;
 
-    // fprintf(stderr, "SEARCHING: %s\n", name);
-    result = avl_search_lock(&host->rrdset_root_index_name, (avl *) (&(tmp.avlname)));
-    if(result) {
-        RRDSET *st = rrdset_from_avlname(result);
-        if(strcmp(st->magic, RRDSET_MAGIC))
-            error("Search for RRDSET %s returned an invalid RRDSET %s (name %s)", name, st->id, st->name);
+        case RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL:
+            return RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL_NAME;
 
-        // fprintf(stderr, "FOUND: %s\n", name);
-        return rrdset_from_avlname(result);
+        case RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL:
+            return RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL_NAME;
     }
-    // fprintf(stderr, "NOT FOUND: %s\n", name);
-    return NULL;
 }
 
 
 // ----------------------------------------------------------------------------
-// RRDDIM index
+// RRD - chart types
 
-static int rrddim_compare(void* a, void* b) {
-    if(((RRDDIM *)a)->hash < ((RRDDIM *)b)->hash) return -1;
-    else if(((RRDDIM *)a)->hash > ((RRDDIM *)b)->hash) return 1;
-    else return strcmp(((RRDDIM *)a)->id, ((RRDDIM *)b)->id);
-}
-
-#define rrddim_index_add(st, rd) (RRDDIM *)avl_insert_lock(&((st)->dimensions_index), (avl *)(rd))
-#define rrddim_index_del(st,rd ) (RRDDIM *)avl_remove_lock(&((st)->dimensions_index), (avl *)(rd))
+inline RRDSET_TYPE rrdset_type_id(const char *name) {
+    if(unlikely(strcmp(name, RRDSET_TYPE_AREA_NAME) == 0))
+        return RRDSET_TYPE_AREA;
 
-static RRDDIM *rrddim_index_find(RRDSET *st, const char *id, uint32_t hash) {
-    RRDDIM tmp;
-    strncpyz(tmp.id, id, RRD_ID_LENGTH_MAX);
-    tmp.hash = (hash)?hash:simple_hash(tmp.id);
+    else if(unlikely(strcmp(name, RRDSET_TYPE_STACKED_NAME) == 0))
+        return RRDSET_TYPE_STACKED;
 
-    return (RRDDIM *)avl_search_lock(&(st->dimensions_index), (avl *) &tmp);
+    else // if(unlikely(strcmp(name, RRDSET_TYPE_LINE_NAME) == 0))
+        return RRDSET_TYPE_LINE;
 }
 
-// ----------------------------------------------------------------------------
-// chart types
-
-int rrdset_type_id(const char *name)
-{
-    if(unlikely(strcmp(name, RRDSET_TYPE_AREA_NAME) == 0)) return RRDSET_TYPE_AREA;
-    else if(unlikely(strcmp(name, RRDSET_TYPE_STACKED_NAME) == 0)) return RRDSET_TYPE_STACKED;
-    else if(unlikely(strcmp(name, RRDSET_TYPE_LINE_NAME) == 0)) return RRDSET_TYPE_LINE;
-    return RRDSET_TYPE_LINE;
-}
-
-const char *rrdset_type_name(int chart_type)
-{
-    static char line[] = RRDSET_TYPE_LINE_NAME;
-    static char area[] = RRDSET_TYPE_AREA_NAME;
-    static char stacked[] = RRDSET_TYPE_STACKED_NAME;
-
+const char *rrdset_type_name(RRDSET_TYPE chart_type) {
     switch(chart_type) {
         case RRDSET_TYPE_LINE:
-            return line;
+        default:
+            return RRDSET_TYPE_LINE_NAME;
 
         case RRDSET_TYPE_AREA:
-            return area;
+            return RRDSET_TYPE_AREA_NAME;
 
         case RRDSET_TYPE_STACKED:
-            return stacked;
+            return RRDSET_TYPE_STACKED_NAME;
     }
-    return line;
 }
 
-// ----------------------------------------------------------------------------
-// load / save
-
-const char *rrd_memory_mode_name(int id)
-{
-    static const char ram[] = RRD_MEMORY_MODE_RAM_NAME;
-    static const char map[] = RRD_MEMORY_MODE_MAP_NAME;
-    static const char save[] = RRD_MEMORY_MODE_SAVE_NAME;
-
-    switch(id) {
-        case RRD_MEMORY_MODE_RAM:
-            return ram;
-
-        case RRD_MEMORY_MODE_MAP:
-            return map;
-
-        case RRD_MEMORY_MODE_SAVE:
-        default:
-            return save;
-    }
-
-    return save;
-}
-
-int rrd_memory_mode_id(const char *name)
-{
-    if(unlikely(!strcmp(name, RRD_MEMORY_MODE_RAM_NAME)))
-        return RRD_MEMORY_MODE_RAM;
-    else if(unlikely(!strcmp(name, RRD_MEMORY_MODE_MAP_NAME)))
-        return RRD_MEMORY_MODE_MAP;
-
-    return RRD_MEMORY_MODE_SAVE;
-}
-
-// ----------------------------------------------------------------------------
-// algorithms types
-
-int rrddim_algorithm_id(const char *name)
-{
-    if(strcmp(name, RRDDIM_INCREMENTAL_NAME) == 0)              return RRDDIM_INCREMENTAL;
-    if(strcmp(name, RRDDIM_ABSOLUTE_NAME) == 0)                 return RRDDIM_ABSOLUTE;
-    if(strcmp(name, RRDDIM_PCENT_OVER_ROW_TOTAL_NAME) == 0)     return RRDDIM_PCENT_OVER_ROW_TOTAL;
-    if(strcmp(name, RRDDIM_PCENT_OVER_DIFF_TOTAL_NAME) == 0)    return RRDDIM_PCENT_OVER_DIFF_TOTAL;
-    return RRDDIM_ABSOLUTE;
-}
-
-const char *rrddim_algorithm_name(int chart_type)
-{
-    static char absolute[] = RRDDIM_ABSOLUTE_NAME;
-    static char incremental[] = RRDDIM_INCREMENTAL_NAME;
-    static char percentage_of_absolute_row[] = RRDDIM_PCENT_OVER_ROW_TOTAL_NAME;
-    static char percentage_of_incremental_row[] = RRDDIM_PCENT_OVER_DIFF_TOTAL_NAME;
-
-    switch(chart_type) {
-        case RRDDIM_ABSOLUTE:
-            return absolute;
-
-        case RRDDIM_INCREMENTAL:
-            return incremental;
-
-        case RRDDIM_PCENT_OVER_ROW_TOTAL:
-            return percentage_of_absolute_row;
-
-        case RRDDIM_PCENT_OVER_DIFF_TOTAL:
-            return percentage_of_incremental_row;
-    }
-    return absolute;
-}
-
-// ----------------------------------------------------------------------------
-// chart names
-
-char *rrdset_strncpyz_name(char *to, const char *from, size_t length)
-{
-    char c, *p = to;
-
-    while (length-- && (c = *from++)) {
-        if(c != '.' && !isalnum(c))
-            c = '_';
-
-        *p++ = c;
-    }
-
-    *p = '\0';
-
-    return to;
-}
-
-void rrdset_set_name(RRDSET *st, const char *name)
-{
-    if(unlikely(st->name && !strcmp(st->name, name)))
-        return;
-
-    debug(D_RRD_CALLS, "rrdset_set_name() old: %s, new: %s", st->name, name);
-
-    char b[CONFIG_MAX_VALUE + 1];
-    char n[RRD_ID_LENGTH_MAX + 1];
-
-    snprintfz(n, RRD_ID_LENGTH_MAX, "%s.%s", st->type, name);
-    rrdset_strncpyz_name(b, n, CONFIG_MAX_VALUE);
-
-    if(st->name) {
-        rrdset_index_del_name(&localhost, st);
-        st->name = config_set_default(st->id, "name", b);
-        st->hash_name = simple_hash(st->name);
-        rrdsetvar_rename_all(st);
-    }
-    else {
-        st->name = config_get(st->id, "name", b);
-        st->hash_name = simple_hash(st->name);
-    }
-
-    pthread_rwlock_wrlock(&st->rwlock);
-    RRDDIM *rd;
-    for(rd = st->dimensions; rd ;rd = rd->next)
-        rrddimvar_rename_all(rd);
-    pthread_rwlock_unlock(&st->rwlock);
-
-    if(unlikely(rrdset_index_add_name(&localhost, st) != st))
-        error("RRDSET: INTERNAL ERROR: attempted to index duplicate chart name '%s'", st->name);
-}
 
 // ----------------------------------------------------------------------------
-// cache directory
+// RRD - cache directory
 
-char *rrdset_cache_dir(const char *id)
-{
+char *rrdset_cache_dir(RRDHOST *host, const char *id, const char *config_section) {
     char *ret = NULL;
 
     char b[FILENAME_MAX + 1];
     char n[FILENAME_MAX + 1];
     rrdset_strncpyz_name(b, id, FILENAME_MAX);
 
-    snprintfz(n, FILENAME_MAX, "%s/%s", netdata_configured_cache_dir, b);
-    ret = config_get(id, "cache directory", n);
+    snprintfz(n, FILENAME_MAX, "%s/%s", host->cache_dir, b);
+    ret = config_get(config_section, "cache directory", n);
 
-    if(rrd_memory_mode == RRD_MEMORY_MODE_MAP || rrd_memory_mode == RRD_MEMORY_MODE_SAVE) {
+    if(host->rrd_memory_mode == RRD_MEMORY_MODE_MAP || host->rrd_memory_mode == RRD_MEMORY_MODE_SAVE) {
         int r = mkdir(ret, 0775);
         if(r != 0 && errno != EEXIST)
             error("Cannot create directory '%s'", ret);
@@ -407,1236 +136,3 @@ char *rrdset_cache_dir(const char *id)
 
     return ret;
 }
-
-// ----------------------------------------------------------------------------
-// core functions
-
-void rrdset_reset(RRDSET *st)
-{
-    debug(D_RRD_CALLS, "rrdset_reset() %s", st->name);
-
-    st->last_collected_time.tv_sec = 0;
-    st->last_collected_time.tv_usec = 0;
-    st->last_updated.tv_sec = 0;
-    st->last_updated.tv_usec = 0;
-    st->current_entry = 0;
-    st->counter = 0;
-    st->counter_done = 0;
-
-    RRDDIM *rd;
-    for(rd = st->dimensions; rd ; rd = rd->next) {
-        rd->last_collected_time.tv_sec = 0;
-        rd->last_collected_time.tv_usec = 0;
-        rd->counter = 0;
-        memset(rd->values, 0, rd->entries * sizeof(storage_number));
-    }
-}
-static inline long align_entries_to_pagesize(long entries) {
-    if(entries < 5) entries = 5;
-    if(entries > RRD_HISTORY_ENTRIES_MAX) entries = RRD_HISTORY_ENTRIES_MAX;
-
-#ifdef NETDATA_LOG_ALLOCATIONS
-    long page = (size_t)sysconf(_SC_PAGESIZE);
-
-    long size = sizeof(RRDDIM) + entries * sizeof(storage_number);
-    if(size % page) {
-        size -= (size % page);
-        size += page;
-
-        long n = (size - sizeof(RRDDIM)) / sizeof(storage_number);
-        return n;
-    }
-
-    return entries;
-#else
-    return entries;
-#endif
-}
-
-static inline void timeval_align(struct timeval *tv, int update_every) {
-    tv->tv_sec -= tv->tv_sec % update_every;
-    tv->tv_usec = 500000;
-}
-
-RRDSET *rrdset_create(const char *type, const char *id, const char *name, const char *family, const char *context, const char *title, const char *units, long priority, int update_every, int chart_type)
-{
-    if(!type || !type[0]) {
-        fatal("Cannot create rrd stats without a type.");
-        return NULL;
-    }
-
-    if(!id || !id[0]) {
-        fatal("Cannot create rrd stats without an id.");
-        return NULL;
-    }
-
-    char fullid[RRD_ID_LENGTH_MAX + 1];
-    char fullfilename[FILENAME_MAX + 1];
-
-    snprintfz(fullid, RRD_ID_LENGTH_MAX, "%s.%s", type, id);
-
-    RRDSET *st = rrdset_find(fullid);
-    if(st) {
-        debug(D_RRD_CALLS, "RRDSET '%s', already exists.", fullid);
-        return st;
-    }
-
-    long rentries = config_get_number(fullid, "history", rrd_default_history_entries);
-    long entries = align_entries_to_pagesize(rentries);
-    if(entries != rentries) entries = config_set_number(fullid, "history", entries);
-
-    int enabled = config_get_boolean(fullid, "enabled", 1);
-    if(!enabled) entries = 5;
-
-    unsigned long size = sizeof(RRDSET);
-    char *cache_dir = rrdset_cache_dir(fullid);
-
-    debug(D_RRD_CALLS, "Creating RRD_STATS for '%s.%s'.", type, id);
-
-    snprintfz(fullfilename, FILENAME_MAX, "%s/main.db", cache_dir);
-    if(rrd_memory_mode != RRD_MEMORY_MODE_RAM) st = (RRDSET *)mymmap(fullfilename, size, ((rrd_memory_mode == RRD_MEMORY_MODE_MAP)?MAP_SHARED:MAP_PRIVATE), 0);
-    if(st) {
-        if(strcmp(st->magic, RRDSET_MAGIC) != 0) {
-            errno = 0;
-            info("Initializing file %s.", fullfilename);
-            memset(st, 0, size);
-        }
-        else if(strcmp(st->id, fullid) != 0) {
-            errno = 0;
-            error("File %s contents are not for chart %s. Clearing it.", fullfilename, fullid);
-            // munmap(st, size);
-            // st = NULL;
-            memset(st, 0, size);
-        }
-        else if(st->memsize != size || st->entries != entries) {
-            errno = 0;
-            error("File %s does not have the desired size. Clearing it.", fullfilename);
-            memset(st, 0, size);
-        }
-        else if(st->update_every != update_every) {
-            errno = 0;
-            error("File %s does not have the desired update frequency. Clearing it.", fullfilename);
-            memset(st, 0, size);
-        }
-        else if((now_realtime_sec() - st->last_updated.tv_sec) > update_every * entries) {
-            errno = 0;
-            error("File %s is too old. Clearing it.", fullfilename);
-            memset(st, 0, size);
-        }
-
-        // make sure the database is aligned
-        if(st->last_updated.tv_sec)
-            timeval_align(&st->last_updated, update_every);
-    }
-
-    if(st) {
-        st->name = NULL;
-        st->type = NULL;
-        st->family = NULL;
-        st->context = NULL;
-        st->title = NULL;
-        st->units = NULL;
-        st->dimensions = NULL;
-        st->next = NULL;
-        st->mapped = rrd_memory_mode;
-        st->variables = NULL;
-        st->alarms = NULL;
-        memset(&st->rwlock, 0, sizeof(pthread_rwlock_t));
-        memset(&st->avl, 0, sizeof(avl));
-        memset(&st->avlname, 0, sizeof(avl));
-        memset(&st->variables_root_index, 0, sizeof(avl_tree_lock));
-        memset(&st->dimensions_index, 0, sizeof(avl_tree_lock));
-    }
-    else {
-        st = callocz(1, size);
-        st->mapped = RRD_MEMORY_MODE_RAM;
-    }
-
-    st->memsize = size;
-    st->entries = entries;
-    st->update_every = update_every;
-
-    if(st->current_entry >= st->entries) st->current_entry = 0;
-
-    strcpy(st->cache_filename, fullfilename);
-    strcpy(st->magic, RRDSET_MAGIC);
-
-    strcpy(st->id, fullid);
-    st->hash = simple_hash(st->id);
-
-    st->cache_dir = cache_dir;
-
-    st->chart_type = rrdset_type_id(config_get(st->id, "chart type", rrdset_type_name(chart_type)));
-    st->type       = config_get(st->id, "type", type);
-    st->family     = config_get(st->id, "family", family?family:st->type);
-    st->units      = config_get(st->id, "units", units?units:"");
-
-    st->context    = config_get(st->id, "context", context?context:st->id);
-    st->hash_context = simple_hash(st->context);
-
-    st->priority = config_get_number(st->id, "priority", priority);
-    st->enabled = enabled;
-
-    st->isdetail = 0;
-    st->debug = 0;
-
-    // if(!strcmp(st->id, "disk_util.dm-0")) {
-    //     st->debug = 1;
-    //     error("enabled debugging for '%s'", st->id);
-    // }
-    // else error("not enabled debugging for '%s'", st->id);
-
-    st->green = NAN;
-    st->red = NAN;
-
-    st->last_collected_time.tv_sec = 0;
-    st->last_collected_time.tv_usec = 0;
-    st->counter_done = 0;
-
-    st->gap_when_lost_iterations_above = (int) (
-            config_get_number(st->id, "gap when lost iterations above", RRD_DEFAULT_GAP_INTERPOLATIONS) + 2);
-
-    avl_init_lock(&st->dimensions_index, rrddim_compare);
-    avl_init_lock(&st->variables_root_index, rrdvar_compare);
-
-    pthread_rwlock_init(&st->rwlock, NULL);
-    rrdhost_rwlock(&localhost);
-
-    if(name && *name) rrdset_set_name(st, name);
-    else rrdset_set_name(st, id);
-
-    {
-        char varvalue[CONFIG_MAX_VALUE + 1];
-        char varvalue2[CONFIG_MAX_VALUE + 1];
-        snprintfz(varvalue, CONFIG_MAX_VALUE, "%s (%s)", title?title:"", st->name);
-        json_escape_string(varvalue2, varvalue, sizeof(varvalue2));
-        st->title = config_get(st->id, "title", varvalue2);
-    }
-
-    st->rrdfamily = rrdfamily_create(st->family);
-    st->rrdhost = &localhost;
-
-    st->next = localhost.rrdset_root;
-    localhost.rrdset_root = st;
-
-    if(health_enabled) {
-        rrdsetvar_create(st, "last_collected_t", RRDVAR_TYPE_TIME_T, &st->last_collected_time.tv_sec, 0);
-        rrdsetvar_create(st, "collected_total_raw", RRDVAR_TYPE_TOTAL, &st->last_collected_total, 0);
-        rrdsetvar_create(st, "green", RRDVAR_TYPE_CALCULATED, &st->green, 0);
-        rrdsetvar_create(st, "red", RRDVAR_TYPE_CALCULATED, &st->red, 0);
-        rrdsetvar_create(st, "update_every", RRDVAR_TYPE_INT, &st->update_every, 0);
-    }
-
-    if(unlikely(rrdset_index_add(&localhost, st) != st))
-        error("RRDSET: INTERNAL ERROR: attempt to index duplicate chart '%s'", st->id);
-
-    rrdsetcalc_link_matching(st);
-    rrdcalctemplate_link_matching(st);
-
-    rrdhost_unlock(&localhost);
-
-    return(st);
-}
-
-RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, long multiplier, long divisor, int algorithm)
-{
-    RRDDIM *rd = rrddim_find(st, id);
-    if(rd) {
-        debug(D_RRD_CALLS, "Cannot create rrd dimension '%s/%s', it already exists.", st->id, name?name:"<NONAME>");
-        return rd;
-    }
-
-    char filename[FILENAME_MAX + 1];
-    char fullfilename[FILENAME_MAX + 1];
-
-    char varname[CONFIG_MAX_NAME + 1];
-    unsigned long size = sizeof(RRDDIM) + (st->entries * sizeof(storage_number));
-
-    debug(D_RRD_CALLS, "Adding dimension '%s/%s'.", st->id, id);
-
-    rrdset_strncpyz_name(filename, id, FILENAME_MAX);
-    snprintfz(fullfilename, FILENAME_MAX, "%s/%s.db", st->cache_dir, filename);
-
-    if(rrd_memory_mode != RRD_MEMORY_MODE_RAM)
-        rd = (RRDDIM *)mymmap(fullfilename, size, ((rrd_memory_mode == RRD_MEMORY_MODE_MAP)?MAP_SHARED:MAP_PRIVATE), 1);
-
-    if(rd) {
-        struct timeval now;
-        now_realtime_timeval(&now);
-
-        if(strcmp(rd->magic, RRDDIMENSION_MAGIC) != 0) {
-            errno = 0;
-            info("Initializing file %s.", fullfilename);
-            memset(rd, 0, size);
-        }
-        else if(rd->memsize != size) {
-            errno = 0;
-            error("File %s does not have the desired size. Clearing it.", fullfilename);
-            memset(rd, 0, size);
-        }
-        else if(rd->multiplier != multiplier) {
-            errno = 0;
-            error("File %s does not have the same multiplier. Clearing it.", fullfilename);
-            memset(rd, 0, size);
-        }
-        else if(rd->divisor != divisor) {
-            errno = 0;
-            error("File %s does not have the same divisor. Clearing it.", fullfilename);
-            memset(rd, 0, size);
-        }
-        else if(rd->update_every != st->update_every) {
-            errno = 0;
-            error("File %s does not have the same refresh frequency. Clearing it.", fullfilename);
-            memset(rd, 0, size);
-        }
-        else if(dt_usec(&now, &rd->last_collected_time) > (rd->entries * rd->update_every * USEC_PER_SEC)) {
-            errno = 0;
-            error("File %s is too old. Clearing it.", fullfilename);
-            memset(rd, 0, size);
-        }
-        else if(strcmp(rd->id, id) != 0) {
-            errno = 0;
-            error("File %s contents are not for dimension %s. Clearing it.", fullfilename, id);
-            // munmap(rd, size);
-            // rd = NULL;
-            memset(rd, 0, size);
-        }
-
-        if(rd->algorithm && rd->algorithm != algorithm)
-            error("File %s does not have the expected algorithm (expected %d '%s', found %d '%s'). Previous values may be wrong.", fullfilename, algorithm, rrddim_algorithm_name(algorithm), rd->algorithm, rrddim_algorithm_name(rd->algorithm));
-    }
-
-    if(rd) {
-        // we have a file mapped for rd
-        rd->mapped = rrd_memory_mode;
-        rd->flags = 0x00000000;
-        rd->variables = NULL;
-        rd->next = NULL;
-        rd->name = NULL;
-        memset(&rd->avl, 0, sizeof(avl));
-    }
-    else {
-        // if we didn't manage to get a mmap'd dimension, just create one
-
-        rd = callocz(1, size);
-        rd->mapped = RRD_MEMORY_MODE_RAM;
-    }
-    rd->memsize = size;
-
-    strcpy(rd->magic, RRDDIMENSION_MAGIC);
-    strcpy(rd->cache_filename, fullfilename);
-    strncpyz(rd->id, id, RRD_ID_LENGTH_MAX);
-    rd->hash = simple_hash(rd->id);
-
-    snprintfz(varname, CONFIG_MAX_NAME, "dim %s name", rd->id);
-    rd->name = config_get(st->id, varname, (name && *name)?name:rd->id);
-
-    snprintfz(varname, CONFIG_MAX_NAME, "dim %s algorithm", rd->id);
-    rd->algorithm = rrddim_algorithm_id(config_get(st->id, varname, rrddim_algorithm_name(algorithm)));
-
-    snprintfz(varname, CONFIG_MAX_NAME, "dim %s multiplier", rd->id);
-    rd->multiplier = config_get_number(st->id, varname, multiplier);
-
-    snprintfz(varname, CONFIG_MAX_NAME, "dim %s divisor", rd->id);
-    rd->divisor = config_get_number(st->id, varname, divisor);
-    if(!rd->divisor) rd->divisor = 1;
-
-    rd->entries = st->entries;
-    rd->update_every = st->update_every;
-
-    // prevent incremental calculation spikes
-    rd->counter = 0;
-    rd->updated = 0;
-    rd->calculated_value = 0;
-    rd->last_calculated_value = 0;
-    rd->collected_value = 0;
-    rd->last_collected_value = 0;
-    rd->collected_volume = 0;
-    rd->stored_volume = 0;
-    rd->last_stored_value = 0;
-    rd->values[st->current_entry] = pack_storage_number(0, SN_NOT_EXISTS);
-    rd->last_collected_time.tv_sec = 0;
-    rd->last_collected_time.tv_usec = 0;
-    rd->rrdset = st;
-
-    // append this dimension
-    pthread_rwlock_wrlock(&st->rwlock);
-    if(!st->dimensions)
-        st->dimensions = rd;
-    else {
-        RRDDIM *td = st->dimensions;
-        for(; td->next; td = td->next) ;
-        td->next = rd;
-    }
-
-    if(health_enabled) {
-        rrddimvar_create(rd, RRDVAR_TYPE_CALCULATED, NULL, NULL, &rd->last_stored_value, 0);
-        rrddimvar_create(rd, RRDVAR_TYPE_COLLECTED, NULL, "_raw", &rd->last_collected_value, 0);
-        rrddimvar_create(rd, RRDVAR_TYPE_TIME_T, NULL, "_last_collected_t", &rd->last_collected_time.tv_sec, 0);
-    }
-
-    pthread_rwlock_unlock(&st->rwlock);
-
-    if(unlikely(rrddim_index_add(st, rd) != rd))
-        error("RRDDIM: INTERNAL ERROR: attempt to index duplicate dimension '%s' on chart '%s'", rd->id, st->id);
-
-    return(rd);
-}
-
-void rrddim_set_name(RRDSET *st, RRDDIM *rd, const char *name)
-{
-    if(unlikely(rd->name && !strcmp(rd->name, name)))
-        return;
-
-    debug(D_RRD_CALLS, "rrddim_set_name() from %s.%s to %s.%s", st->name, rd->name, st->name, name);
-
-    char varname[CONFIG_MAX_NAME + 1];
-    snprintfz(varname, CONFIG_MAX_NAME, "dim %s name", rd->id);
-    rd->name = config_set_default(st->id, varname, name);
-
-    rrddimvar_rename_all(rd);
-}
-
-void rrddim_free(RRDSET *st, RRDDIM *rd)
-{
-    debug(D_RRD_CALLS, "rrddim_free() %s.%s", st->name, rd->name);
-
-    if(rd == st->dimensions)
-        st->dimensions = rd->next;
-    else {
-        RRDDIM *i;
-        for (i = st->dimensions; i && i->next != rd; i = i->next) ;
-
-        if (i && i->next == rd)
-            i->next = rd->next;
-        else
-            error("Request to free dimension '%s.%s' but it is not linked.", st->id, rd->name);
-    }
-    rd->next = NULL;
-
-    while(rd->variables)
-        rrddimvar_free(rd->variables);
-
-    if(unlikely(rrddim_index_del(st, rd) != rd))
-        error("RRDDIM: INTERNAL ERROR: attempt to remove from index dimension '%s' on chart '%s', removed a different dimension.", rd->id, st->id);
-
-    // free(rd->annotations);
-    if(rd->mapped == RRD_MEMORY_MODE_SAVE) {
-        debug(D_RRD_CALLS, "Saving dimension '%s' to '%s'.", rd->name, rd->cache_filename);
-        savememory(rd->cache_filename, rd, rd->memsize);
-
-        debug(D_RRD_CALLS, "Unmapping dimension '%s'.", rd->name);
-        munmap(rd, rd->memsize);
-    }
-    else if(rd->mapped == RRD_MEMORY_MODE_MAP) {
-        debug(D_RRD_CALLS, "Unmapping dimension '%s'.", rd->name);
-        munmap(rd, rd->memsize);
-    }
-    else {
-        debug(D_RRD_CALLS, "Removing dimension '%s'.", rd->name);
-        freez(rd);
-    }
-}
-
-void rrdset_free_all(void)
-{
-    info("Freeing all memory...");
-
-    rrdhost_rwlock(&localhost);
-
-    RRDSET *st;
-    for(st = localhost.rrdset_root; st ;) {
-        RRDSET *next = st->next;
-
-        pthread_rwlock_wrlock(&st->rwlock);
-
-        while(st->variables)
-            rrdsetvar_free(st->variables);
-
-        while(st->alarms)
-            rrdsetcalc_unlink(st->alarms);
-
-        while(st->dimensions)
-            rrddim_free(st, st->dimensions);
-
-        if(unlikely(rrdset_index_del(&localhost, st) != st))
-            error("RRDSET: INTERNAL ERROR: attempt to remove from index chart '%s', removed a different chart.", st->id);
-
-        rrdset_index_del_name(&localhost, st);
-
-        st->rrdfamily->use_count--;
-        if(!st->rrdfamily->use_count)
-            rrdfamily_free(st->rrdfamily);
-
-        pthread_rwlock_unlock(&st->rwlock);
-
-        if(st->mapped == RRD_MEMORY_MODE_SAVE || st->mapped == RRD_MEMORY_MODE_MAP) {
-            debug(D_RRD_CALLS, "Unmapping stats '%s'.", st->name);
-            munmap(st, st->memsize);
-        }
-        else
-            freez(st);
-
-        st = next;
-    }
-    localhost.rrdset_root = NULL;
-
-    rrdhost_unlock(&localhost);
-
-    info("Memory cleanup completed...");
-}
-
-void rrdset_save_all(void) {
-    info("Saving database...");
-
-    RRDSET *st;
-    RRDDIM *rd;
-
-    // we get an write lock
-    // to ensure only one thread is saving the database
-    rrdhost_rwlock(&localhost);
-
-    for(st = localhost.rrdset_root; st ; st = st->next) {
-        pthread_rwlock_rdlock(&st->rwlock);
-
-        if(st->mapped == RRD_MEMORY_MODE_SAVE) {
-            debug(D_RRD_CALLS, "Saving stats '%s' to '%s'.", st->name, st->cache_filename);
-            savememory(st->cache_filename, st, st->memsize);
-        }
-
-        for(rd = st->dimensions; rd ; rd = rd->next) {
-            if(likely(rd->mapped == RRD_MEMORY_MODE_SAVE)) {
-                debug(D_RRD_CALLS, "Saving dimension '%s' to '%s'.", rd->name, rd->cache_filename);
-                savememory(rd->cache_filename, rd, rd->memsize);
-            }
-        }
-
-        pthread_rwlock_unlock(&st->rwlock);
-    }
-
-    rrdhost_unlock(&localhost);
-}
-
-
-RRDSET *rrdset_find(const char *id)
-{
-    debug(D_RRD_CALLS, "rrdset_find() for chart %s", id);
-
-    RRDSET *st = rrdset_index_find(&localhost, id, 0);
-    return(st);
-}
-
-RRDSET *rrdset_find_bytype(const char *type, const char *id)
-{
-    debug(D_RRD_CALLS, "rrdset_find_bytype() for chart %s.%s", type, id);
-
-    char buf[RRD_ID_LENGTH_MAX + 1];
-
-    strncpyz(buf, type, RRD_ID_LENGTH_MAX - 1);
-    strcat(buf, ".");
-    int len = (int) strlen(buf);
-    strncpyz(&buf[len], id, (size_t) (RRD_ID_LENGTH_MAX - len));
-
-    return(rrdset_find(buf));
-}
-
-RRDSET *rrdset_find_byname(const char *name)
-{
-    debug(D_RRD_CALLS, "rrdset_find_byname() for chart %s", name);
-
-    RRDSET *st = rrdset_index_find_name(&localhost, name, 0);
-    return(st);
-}
-
-RRDDIM *rrddim_find(RRDSET *st, const char *id)
-{
-    debug(D_RRD_CALLS, "rrddim_find() for chart %s, dimension %s", st->name, id);
-
-    return rrddim_index_find(st, id, 0);
-}
-
-int rrddim_hide(RRDSET *st, const char *id)
-{
-    debug(D_RRD_CALLS, "rrddim_hide() for chart %s, dimension %s", st->name, id);
-
-    RRDDIM *rd = rrddim_find(st, id);
-    if(unlikely(!rd)) {
-        error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
-        return 1;
-    }
-
-    rd->flags |= RRDDIM_FLAG_HIDDEN;
-    return 0;
-}
-
-int rrddim_unhide(RRDSET *st, const char *id)
-{
-    debug(D_RRD_CALLS, "rrddim_unhide() for chart %s, dimension %s", st->name, id);
-
-    RRDDIM *rd = rrddim_find(st, id);
-    if(unlikely(!rd)) {
-        error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
-        return 1;
-    }
-
-    if(rd->flags & RRDDIM_FLAG_HIDDEN) rd->flags ^= RRDDIM_FLAG_HIDDEN;
-    return 0;
-}
-
-collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value)
-{
-    debug(D_RRD_CALLS, "rrddim_set_by_pointer() for chart %s, dimension %s, value " COLLECTED_NUMBER_FORMAT, st->name, rd->name, value);
-
-    now_realtime_timeval(&rd->last_collected_time);
-    rd->collected_value = value;
-    rd->updated = 1;
-    rd->counter++;
-
-    // fprintf(stderr, "%s.%s %llu " COLLECTED_NUMBER_FORMAT " dt %0.6f" " rate " CALCULATED_NUMBER_FORMAT "\n", st->name, rd->name, st->usec_since_last_update, value, (float)((double)st->usec_since_last_update / (double)1000000), (calculated_number)((value - rd->last_collected_value) * (calculated_number)rd->multiplier / (calculated_number)rd->divisor * 1000000.0 / (calculated_number)st->usec_since_last_update));
-
-    return rd->last_collected_value;
-}
-
-collected_number rrddim_set(RRDSET *st, const char *id, collected_number value)
-{
-    RRDDIM *rd = rrddim_find(st, id);
-    if(unlikely(!rd)) {
-        error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
-        return 0;
-    }
-
-    return rrddim_set_by_pointer(st, rd, value);
-}
-
-void rrdset_next_usec_unfiltered(RRDSET *st, usec_t microseconds)
-{
-    if(unlikely(!st->last_collected_time.tv_sec || !microseconds)) {
-        // the first entry
-        microseconds = st->update_every * USEC_PER_SEC;
-    }
-    st->usec_since_last_update = microseconds;
-}
-
-void rrdset_next_usec(RRDSET *st, usec_t microseconds)
-{
-    struct timeval now;
-    now_realtime_timeval(&now);
-
-    if(unlikely(!st->last_collected_time.tv_sec)) {
-        // the first entry
-        microseconds = st->update_every * USEC_PER_SEC;
-    }
-    else if(unlikely(!microseconds)) {
-        // no dt given by the plugin
-        microseconds = dt_usec(&now, &st->last_collected_time);
-    }
-    else {
-        // microseconds has the time since the last collection
-#ifdef NETDATA_INTERNAL_CHECKS
-        usec_t now_usec = timeval_usec(&now);
-        usec_t last_usec = timeval_usec(&st->last_collected_time);
-#endif
-        usec_t since_last_usec = dt_usec(&now, &st->last_collected_time);
-
-        // verify the microseconds given is good
-        if(unlikely(microseconds > since_last_usec)) {
-            debug(D_RRD_CALLS, "dt %llu usec given is too big - it leads %llu usec to the future, for chart '%s' (%s).", microseconds, microseconds - since_last_usec, st->name, st->id);
-
-#ifdef NETDATA_INTERNAL_CHECKS
-            if(unlikely(last_usec + microseconds > now_usec + 1000))
-                error("dt %llu usec given is too big - it leads %llu usec to the future, for chart '%s' (%s).", microseconds, microseconds - since_last_usec, st->name, st->id);
-#endif
-
-            microseconds = since_last_usec;
-        }
-        else if(unlikely(microseconds < since_last_usec * 0.8)) {
-            debug(D_RRD_CALLS, "dt %llu usec given is too small - expected %llu usec up to -20%%, for chart '%s' (%s).", microseconds, since_last_usec, st->name, st->id);
-
-#ifdef NETDATA_INTERNAL_CHECKS
-            error("dt %llu usec given is too small - expected %llu usec up to -20%%, for chart '%s' (%s).", microseconds, since_last_usec, st->name, st->id);
-#endif
-            microseconds = since_last_usec;
-        }
-    }
-    debug(D_RRD_CALLS, "rrdset_next_usec() for chart %s with microseconds %llu", st->name, microseconds);
-
-    if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: NEXT: %llu microseconds", st->name, microseconds);
-    st->usec_since_last_update = microseconds;
-}
-
-usec_t rrdset_done(RRDSET *st)
-{
-    if(unlikely(netdata_exit)) return 0;
-
-    debug(D_RRD_CALLS, "rrdset_done() for chart %s", st->name);
-
-    RRDDIM *rd;
-
-    int
-        pthreadoldcancelstate;  // store the old cancelable pthread state, to restore it at the end
-
-    char
-        store_this_entry = 1,   // boolean: 1 = store this entry, 0 = don't store this entry
-        first_entry = 0;        // boolean: 1 = this is the first entry seen for this chart, 0 = all other entries
-
-    unsigned int
-        stored_entries = 0;     // the number of entries we have stored in the db, during this call to rrdset_done()
-
-    usec_t
-        last_collect_ut,        // the timestamp in microseconds, of the last collected value
-        now_collect_ut,         // the timestamp in microseconds, of this collected value (this is NOW)
-        last_stored_ut,         // the timestamp in microseconds, of the last stored entry in the db
-        next_store_ut,          // the timestamp in microseconds, of the next entry to store in the db
-        update_every_ut = st->update_every * USEC_PER_SEC; // st->update_every in microseconds
-
-    if(unlikely(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &pthreadoldcancelstate) != 0))
-        error("Cannot set pthread cancel state to DISABLE.");
-
-    // a read lock is OK here
-    pthread_rwlock_rdlock(&st->rwlock);
-
-/*
-    // enable the chart, if it was disabled
-    if(unlikely(rrd_delete_unupdated_dimensions) && !st->enabled)
-        st->enabled = 1;
-*/
-
-    // check if the chart has a long time to be updated
-    if(unlikely(st->usec_since_last_update > st->entries * update_every_ut)) {
-        info("%s: took too long to be updated (%0.3Lf secs). Resetting it.", st->name, (long double)(st->usec_since_last_update / 1000000.0));
-        rrdset_reset(st);
-        st->usec_since_last_update = update_every_ut;
-        first_entry = 1;
-    }
-    if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: microseconds since last update: %llu", st->name, st->usec_since_last_update);
-
-    // set last_collected_time
-    if(unlikely(!st->last_collected_time.tv_sec)) {
-        // it is the first entry
-        // set the last_collected_time to now
-        now_realtime_timeval(&st->last_collected_time);
-        timeval_align(&st->last_collected_time, st->update_every);
-
-        last_collect_ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec - update_every_ut;
-
-        // the first entry should not be stored
-        store_this_entry = 0;
-        first_entry = 1;
-
-        if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: has not set last_collected_time. Setting it now. Will not store the next entry.", st->name);
-    }
-    else {
-        // it is not the first entry
-        // calculate the proper last_collected_time, using usec_since_last_update
-        last_collect_ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec;
-        usec_t ut = last_collect_ut + st->usec_since_last_update;
-        st->last_collected_time.tv_sec = (time_t) (ut / USEC_PER_SEC);
-        st->last_collected_time.tv_usec = (suseconds_t) (ut % USEC_PER_SEC);
-    }
-
-    // if this set has not been updated in the past
-    // we fake the last_update time to be = now - usec_since_last_update
-    if(unlikely(!st->last_updated.tv_sec)) {
-        // it has never been updated before
-        // set a fake last_updated, in the past using usec_since_last_update
-        usec_t ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec - st->usec_since_last_update;
-        st->last_updated.tv_sec = (time_t) (ut / USEC_PER_SEC);
-        st->last_updated.tv_usec = (suseconds_t) (ut % USEC_PER_SEC);
-
-        // the first entry should not be stored
-        store_this_entry = 0;
-        first_entry = 1;
-
-        if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: initializing last_updated to now - %llu microseconds (%0.3Lf). Will not store the next entry.", st->name, st->usec_since_last_update, (long double)ut/1000000.0);
-    }
-
-    // check if we will re-write the entire data set
-    if(unlikely(dt_usec(&st->last_collected_time, &st->last_updated) > st->entries * update_every_ut)) {
-        info("%s: too old data (last updated at %ld.%ld, last collected at %ld.%ld). Resetting it. Will not store the next entry.", st->name, st->last_updated.tv_sec, st->last_updated.tv_usec, st->last_collected_time.tv_sec, st->last_collected_time.tv_usec);
-        rrdset_reset(st);
-
-        st->usec_since_last_update = update_every_ut;
-
-        now_realtime_timeval(&st->last_collected_time);
-        timeval_align(&st->last_collected_time, st->update_every);
-
-        usec_t ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec - st->usec_since_last_update;
-        st->last_updated.tv_sec = (time_t) (ut / USEC_PER_SEC);
-        st->last_updated.tv_usec = (suseconds_t) (ut % USEC_PER_SEC);
-
-        // the first entry should not be stored
-        store_this_entry = 0;
-        first_entry = 1;
-    }
-
-    // these are the 3 variables that will help us in interpolation
-    // last_stored_ut = the last time we added a value to the storage
-    // now_collect_ut = the time the current value has been collected
-    // next_store_ut  = the time of the next interpolation point
-    last_stored_ut = st->last_updated.tv_sec * USEC_PER_SEC + st->last_updated.tv_usec;
-    now_collect_ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec;
-    next_store_ut  = (st->last_updated.tv_sec + st->update_every) * USEC_PER_SEC;
-
-    if(unlikely(st->debug)) {
-        debug(D_RRD_STATS, "%s: last_collect_ut = %0.3Lf (last collection time)", st->name, (long double)last_collect_ut/1000000.0);
-        debug(D_RRD_STATS, "%s: now_collect_ut  = %0.3Lf (current collection time)", st->name, (long double)now_collect_ut/1000000.0);
-        debug(D_RRD_STATS, "%s: last_stored_ut  = %0.3Lf (last updated time)", st->name, (long double)last_stored_ut/1000000.0);
-        debug(D_RRD_STATS, "%s: next_store_ut   = %0.3Lf (next interpolation point)", st->name, (long double)next_store_ut/1000000.0);
-    }
-
-    if(unlikely(!st->counter_done)) {
-        store_this_entry = 0;
-        if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: Will not store the next entry.", st->name);
-    }
-    st->counter_done++;
-
-    // calculate totals and count the dimensions
-    int dimensions;
-    st->collected_total = 0;
-    for( rd = st->dimensions, dimensions = 0 ; rd ; rd = rd->next, dimensions++ )
-        if(likely(rd->updated)) st->collected_total += rd->collected_value;
-
-    uint32_t storage_flags = SN_EXISTS;
-
-    // process all dimensions to calculate their values
-    // based on the collected figures only
-    // at this stage we do not interpolate anything
-    for( rd = st->dimensions ; rd ; rd = rd->next ) {
-
-        if(unlikely(!rd->updated)) {
-            rd->calculated_value = 0;
-            continue;
-        }
-
-        if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: START "
-            " last_collected_value = " COLLECTED_NUMBER_FORMAT
-            " collected_value = " COLLECTED_NUMBER_FORMAT
-            " last_calculated_value = " CALCULATED_NUMBER_FORMAT
-            " calculated_value = " CALCULATED_NUMBER_FORMAT
-            , st->id, rd->name
-            , rd->last_collected_value
-            , rd->collected_value
-            , rd->last_calculated_value
-            , rd->calculated_value
-            );
-
-        switch(rd->algorithm) {
-            case RRDDIM_ABSOLUTE:
-                rd->calculated_value = (calculated_number)rd->collected_value
-                    * (calculated_number)rd->multiplier
-                    / (calculated_number)rd->divisor;
-
-                if(unlikely(st->debug))
-                    debug(D_RRD_STATS, "%s/%s: CALC ABS/ABS-NO-IN "
-                        CALCULATED_NUMBER_FORMAT " = "
-                        COLLECTED_NUMBER_FORMAT
-                        " * " CALCULATED_NUMBER_FORMAT
-                        " / " CALCULATED_NUMBER_FORMAT
-                        , st->id, rd->name
-                        , rd->calculated_value
-                        , rd->collected_value
-                        , (calculated_number)rd->multiplier
-                        , (calculated_number)rd->divisor
-                        );
-                break;
-
-            case RRDDIM_PCENT_OVER_ROW_TOTAL:
-                if(unlikely(!st->collected_total))
-                    rd->calculated_value = 0;
-                else
-                    // the percentage of the current value
-                    // over the total of all dimensions
-                    rd->calculated_value =
-                          (calculated_number)100
-                        * (calculated_number)rd->collected_value
-                        / (calculated_number)st->collected_total;
-
-                if(unlikely(st->debug))
-                    debug(D_RRD_STATS, "%s/%s: CALC PCENT-ROW "
-                        CALCULATED_NUMBER_FORMAT " = 100"
-                        " * " COLLECTED_NUMBER_FORMAT
-                        " / " COLLECTED_NUMBER_FORMAT
-                        , st->id, rd->name
-                        , rd->calculated_value
-                        , rd->collected_value
-                        , st->collected_total
-                        );
-                break;
-
-            case RRDDIM_INCREMENTAL:
-                if(unlikely(rd->counter <= 1)) {
-                    rd->calculated_value = 0;
-                    continue;
-                }
-
-                // if the new is smaller than the old (an overflow, or reset), set the old equal to the new
-                // to reset the calculation (it will give zero as the calculation for this second)
-                if(unlikely(rd->last_collected_value > rd->collected_value)) {
-                    debug(D_RRD_STATS, "%s.%s: RESET or OVERFLOW. Last collected value = " COLLECTED_NUMBER_FORMAT ", current = " COLLECTED_NUMBER_FORMAT
-                            , st->name, rd->name
-                            , rd->last_collected_value
-                            , rd->collected_value);
-                    if(!(rd->flags & RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS)) storage_flags = SN_EXISTS_RESET;
-                    rd->last_collected_value = rd->collected_value;
-                }
-
-                rd->calculated_value +=
-                      (calculated_number)(rd->collected_value - rd->last_collected_value)
-                    * (calculated_number)rd->multiplier
-                    / (calculated_number)rd->divisor;
-
-                if(unlikely(st->debug))
-                    debug(D_RRD_STATS, "%s/%s: CALC INC PRE "
-                        CALCULATED_NUMBER_FORMAT " = ("
-                        COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT
-                        ")"
-                        " * " CALCULATED_NUMBER_FORMAT
-                        " / " CALCULATED_NUMBER_FORMAT
-                        , st->id, rd->name
-                        , rd->calculated_value
-                        , rd->collected_value, rd->last_collected_value
-                        , (calculated_number)rd->multiplier
-                        , (calculated_number)rd->divisor
-                        );
-                break;
-
-            case RRDDIM_PCENT_OVER_DIFF_TOTAL:
-                if(unlikely(rd->counter <= 1)) {
-                    rd->calculated_value = 0;
-                    continue;
-                }
-
-                // if the new is smaller than the old (an overflow, or reset), set the old equal to the new
-                // to reset the calculation (it will give zero as the calculation for this second)
-                if(unlikely(rd->last_collected_value > rd->collected_value)) {
-                    debug(D_RRD_STATS, "%s.%s: RESET or OVERFLOW. Last collected value = " COLLECTED_NUMBER_FORMAT ", current = " COLLECTED_NUMBER_FORMAT
-                    , st->name, rd->name
-                    , rd->last_collected_value
-                    , rd->collected_value);
-                    if(!(rd->flags & RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS)) storage_flags = SN_EXISTS_RESET;
-                    rd->last_collected_value = rd->collected_value;
-                }
-
-                // the percentage of the current increment
-                // over the increment of all dimensions together
-                if(unlikely(st->collected_total == st->last_collected_total))
-                    rd->calculated_value = 0;
-                else
-                    rd->calculated_value =
-                          (calculated_number)100
-                        * (calculated_number)(rd->collected_value - rd->last_collected_value)
-                        / (calculated_number)(st->collected_total - st->last_collected_total);
-
-                if(unlikely(st->debug))
-                    debug(D_RRD_STATS, "%s/%s: CALC PCENT-DIFF "
-                        CALCULATED_NUMBER_FORMAT " = 100"
-                        " * (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")"
-                        " / (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")"
-                        , st->id, rd->name
-                        , rd->calculated_value
-                        , rd->collected_value, rd->last_collected_value
-                        , st->collected_total, st->last_collected_total
-                        );
-                break;
-
-            default:
-                // make the default zero, to make sure
-                // it gets noticed when we add new types
-                rd->calculated_value = 0;
-
-                if(unlikely(st->debug))
-                    debug(D_RRD_STATS, "%s/%s: CALC "
-                        CALCULATED_NUMBER_FORMAT " = 0"
-                        , st->id, rd->name
-                        , rd->calculated_value
-                        );
-                break;
-        }
-
-        if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: PHASE2 "
-            " last_collected_value = " COLLECTED_NUMBER_FORMAT
-            " collected_value = " COLLECTED_NUMBER_FORMAT
-            " last_calculated_value = " CALCULATED_NUMBER_FORMAT
-            " calculated_value = " CALCULATED_NUMBER_FORMAT
-            , st->id, rd->name
-            , rd->last_collected_value
-            , rd->collected_value
-            , rd->last_calculated_value
-            , rd->calculated_value
-            );
-
-    }
-
-    // at this point we have all the calculated values ready
-    // it is now time to interpolate values on a second boundary
-
-    if(unlikely(now_collect_ut < next_store_ut)) {
-        // this is collected in the same interpolation point
-        if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: THIS IS IN THE SAME INTERPOLATION POINT", st->name);
-#ifdef NETDATA_INTERNAL_CHECKS
-        info("%s is collected in the same interpolation point: short by %llu microseconds", st->name, next_store_ut - now_collect_ut);
-#endif
-    }
-
-    usec_t first_ut = last_stored_ut;
-    long long iterations = (now_collect_ut - last_stored_ut) / (update_every_ut);
-    if((now_collect_ut % (update_every_ut)) == 0) iterations++;
-
-    for( ; next_store_ut <= now_collect_ut ; last_collect_ut = next_store_ut, next_store_ut += update_every_ut, iterations-- ) {
-#ifdef NETDATA_INTERNAL_CHECKS
-        if(iterations < 0) { error("%s: iterations calculation wrapped! first_ut = %llu, last_stored_ut = %llu, next_store_ut = %llu, now_collect_ut = %llu", st->name, first_ut, last_stored_ut, next_store_ut, now_collect_ut); }
-#endif
-
-        if(unlikely(st->debug)) {
-            debug(D_RRD_STATS, "%s: last_stored_ut = %0.3Lf (last updated time)", st->name, (long double)last_stored_ut/1000000.0);
-            debug(D_RRD_STATS, "%s: next_store_ut  = %0.3Lf (next interpolation point)", st->name, (long double)next_store_ut/1000000.0);
-        }
-
-        st->last_updated.tv_sec = (time_t) (next_store_ut / USEC_PER_SEC);
-        st->last_updated.tv_usec = 0;
-
-        for( rd = st->dimensions ; likely(rd) ; rd = rd->next ) {
-            calculated_number new_value;
-
-            switch(rd->algorithm) {
-                case RRDDIM_INCREMENTAL:
-                    new_value = (calculated_number)
-                        (      rd->calculated_value
-                            * (calculated_number)(next_store_ut - last_collect_ut)
-                            / (calculated_number)(now_collect_ut - last_collect_ut)
-                        );
-
-                    if(unlikely(st->debug))
-                        debug(D_RRD_STATS, "%s/%s: CALC2 INC "
-                            CALCULATED_NUMBER_FORMAT " = "
-                            CALCULATED_NUMBER_FORMAT
-                            " * %llu"
-                            " / %llu"
-                            , st->id, rd->name
-                            , new_value
-                            , rd->calculated_value
-                            , (next_store_ut - last_stored_ut)
-                            , (now_collect_ut - last_stored_ut)
-                            );
-
-                    rd->calculated_value -= new_value;
-                    new_value += rd->last_calculated_value;
-                    rd->last_calculated_value = 0;
-                    new_value /= (calculated_number)st->update_every;
-
-                    if(unlikely(next_store_ut - last_stored_ut < update_every_ut)) {
-                        if(unlikely(st->debug))
-                            debug(D_RRD_STATS, "%s/%s: COLLECTION POINT IS SHORT " CALCULATED_NUMBER_FORMAT " - EXTRAPOLATING",
-                                st->id, rd->name
-                                , (calculated_number)(next_store_ut - last_stored_ut)
-                                );
-                        new_value = new_value * (calculated_number)(st->update_every * 1000000) / (calculated_number)(next_store_ut - last_stored_ut);
-                    }
-                    break;
-
-                case RRDDIM_ABSOLUTE:
-                case RRDDIM_PCENT_OVER_ROW_TOTAL:
-                case RRDDIM_PCENT_OVER_DIFF_TOTAL:
-                default:
-                    if(iterations == 1) {
-                        // this is the last iteration
-                        // do not interpolate
-                        // just show the calculated value
-
-                        new_value = rd->calculated_value;
-                    }
-                    else {
-                        // we have missed an update
-                        // interpolate in the middle values
-
-                        new_value = (calculated_number)
-                            (   (     (rd->calculated_value - rd->last_calculated_value)
-                                    * (calculated_number)(next_store_ut - last_collect_ut)
-                                    / (calculated_number)(now_collect_ut - last_collect_ut)
-                                )
-                                +  rd->last_calculated_value
-                            );
-
-                        if(unlikely(st->debug))
-                            debug(D_RRD_STATS, "%s/%s: CALC2 DEF "
-                                CALCULATED_NUMBER_FORMAT " = ((("
-                                "(" CALCULATED_NUMBER_FORMAT " - " CALCULATED_NUMBER_FORMAT ")"
-                                " * %llu"
-                                " / %llu) + " CALCULATED_NUMBER_FORMAT
-                                , st->id, rd->name
-                                , new_value
-                                , rd->calculated_value, rd->last_calculated_value
-                                , (next_store_ut - first_ut)
-                                , (now_collect_ut - first_ut), rd->last_calculated_value
-                                );
-                    }
-                    break;
-            }
-
-            if(unlikely(!store_this_entry)) {
-                rd->values[st->current_entry] = pack_storage_number(0, SN_NOT_EXISTS);
-                continue;
-            }
-
-            if(likely(rd->updated && rd->counter > 1 && iterations < st->gap_when_lost_iterations_above)) {
-                rd->values[st->current_entry] = pack_storage_number(new_value, storage_flags );
-                rd->last_stored_value = new_value;
-
-                if(unlikely(st->debug))
-                    debug(D_RRD_STATS, "%s/%s: STORE[%ld] "
-                        CALCULATED_NUMBER_FORMAT " = " CALCULATED_NUMBER_FORMAT
-                        , st->id, rd->name
-                        , st->current_entry
-                        , unpack_storage_number(rd->values[st->current_entry]), new_value
-                        );
-            }
-            else {
-                if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: STORE[%ld] = NON EXISTING "
-                        , st->id, rd->name
-                        , st->current_entry
-                        );
-                rd->values[st->current_entry] = pack_storage_number(0, SN_NOT_EXISTS);
-                rd->last_stored_value = NAN;
-            }
-
-            stored_entries++;
-
-            if(unlikely(st->debug)) {
-                calculated_number t1 = new_value * (calculated_number)rd->multiplier / (calculated_number)rd->divisor;
-                calculated_number t2 = unpack_storage_number(rd->values[st->current_entry]);
-                calculated_number accuracy = accuracy_loss(t1, t2);
-                debug(D_RRD_STATS, "%s/%s: UNPACK[%ld] = " CALCULATED_NUMBER_FORMAT " FLAGS=0x%08x (original = " CALCULATED_NUMBER_FORMAT ", accuracy loss = " CALCULATED_NUMBER_FORMAT "%%%s)"
-                        , st->id, rd->name
-                        , st->current_entry
-                        , t2
-                        , get_storage_number_flags(rd->values[st->current_entry])
-                        , t1
-                        , accuracy
-                        , (accuracy > ACCURACY_LOSS) ? " **TOO BIG** " : ""
-                        );
-
-                rd->collected_volume += t1;
-                rd->stored_volume += t2;
-                accuracy = accuracy_loss(rd->collected_volume, rd->stored_volume);
-                debug(D_RRD_STATS, "%s/%s: VOLUME[%ld] = " CALCULATED_NUMBER_FORMAT ", calculated  = " CALCULATED_NUMBER_FORMAT ", accuracy loss = " CALCULATED_NUMBER_FORMAT "%%%s"
-                        , st->id, rd->name
-                        , st->current_entry
-                        , rd->stored_volume
-                        , rd->collected_volume
-                        , accuracy
-                        , (accuracy > ACCURACY_LOSS) ? " **TOO BIG** " : ""
-                        );
-
-            }
-        }
-        // reset the storage flags for the next point, if any;
-        storage_flags = SN_EXISTS;
-
-        st->counter++;
-        st->current_entry = ((st->current_entry + 1) >= st->entries) ? 0 : st->current_entry + 1;
-        last_stored_ut = next_store_ut;
-    }
-
-    st->last_collected_total  = st->collected_total;
-
-    for( rd = st->dimensions; rd ; rd = rd->next ) {
-        if(unlikely(!rd->updated)) continue;
-
-        if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: setting last_collected_value (old: " COLLECTED_NUMBER_FORMAT ") to last_collected_value (new: " COLLECTED_NUMBER_FORMAT ")", st->id, rd->name, rd->last_collected_value, rd->collected_value);
-        rd->last_collected_value = rd->collected_value;
-
-        switch(rd->algorithm) {
-            case RRDDIM_INCREMENTAL:
-                if(unlikely(!first_entry)) {
-                    if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: setting last_calculated_value (old: " CALCULATED_NUMBER_FORMAT ") to last_calculated_value (new: " CALCULATED_NUMBER_FORMAT ")", st->id, rd->name, rd->last_calculated_value + rd->calculated_value, rd->calculated_value);
-                    rd->last_calculated_value += rd->calculated_value;
-                }
-                else {
-                    if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: THIS IS THE FIRST POINT", st->name);
-                }
-                break;
-
-            case RRDDIM_ABSOLUTE:
-            case RRDDIM_PCENT_OVER_ROW_TOTAL:
-            case RRDDIM_PCENT_OVER_DIFF_TOTAL:
-                if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: setting last_calculated_value (old: " CALCULATED_NUMBER_FORMAT ") to last_calculated_value (new: " CALCULATED_NUMBER_FORMAT ")", st->id, rd->name, rd->last_calculated_value, rd->calculated_value);
-                rd->last_calculated_value = rd->calculated_value;
-                break;
-        }
-
-        rd->calculated_value = 0;
-        rd->collected_value = 0;
-        rd->updated = 0;
-
-        if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: END "
-            " last_collected_value = " COLLECTED_NUMBER_FORMAT
-            " collected_value = " COLLECTED_NUMBER_FORMAT
-            " last_calculated_value = " CALCULATED_NUMBER_FORMAT
-            " calculated_value = " CALCULATED_NUMBER_FORMAT
-            , st->id, rd->name
-            , rd->last_collected_value
-            , rd->collected_value
-            , rd->last_calculated_value
-            , rd->calculated_value
-            );
-    }
-
-    // ALL DONE ABOUT THE DATA UPDATE
-    // --------------------------------------------------------------------
-
-/*
-    // find if there are any obsolete dimensions (not updated recently)
-    if(unlikely(rrd_delete_unupdated_dimensions)) {
-
-        for( rd = st->dimensions; likely(rd) ; rd = rd->next )
-            if((rd->last_collected_time.tv_sec + (rrd_delete_unupdated_dimensions * st->update_every)) < st->last_collected_time.tv_sec)
-                break;
-
-        if(unlikely(rd)) {
-            RRDDIM *last;
-            // there is dimension to free
-            // upgrade our read lock to a write lock
-            pthread_rwlock_unlock(&st->rwlock);
-            pthread_rwlock_wrlock(&st->rwlock);
-
-            for( rd = st->dimensions, last = NULL ; likely(rd) ; ) {
-                // remove it only it is not updated in rrd_delete_unupdated_dimensions seconds
-
-                if(unlikely((rd->last_collected_time.tv_sec + (rrd_delete_unupdated_dimensions * st->update_every)) < st->last_collected_time.tv_sec)) {
-                    info("Removing obsolete dimension '%s' (%s) of '%s' (%s).", rd->name, rd->id, st->name, st->id);
-
-                    if(unlikely(!last)) {
-                        st->dimensions = rd->next;
-                        rd->next = NULL;
-                        rrddim_free(st, rd);
-                        rd = st->dimensions;
-                        continue;
-                    }
-                    else {
-                        last->next = rd->next;
-                        rd->next = NULL;
-                        rrddim_free(st, rd);
-                        rd = last->next;
-                        continue;
-                    }
-                }
-
-                last = rd;
-                rd = rd->next;
-            }
-
-            if(unlikely(!st->dimensions)) {
-                info("Disabling chart %s (%s) since it does not have any dimensions", st->name, st->id);
-                st->enabled = 0;
-            }
-        }
-    }
-*/
-
-    pthread_rwlock_unlock(&st->rwlock);
-
-    if(unlikely(pthread_setcancelstate(pthreadoldcancelstate, NULL) != 0))
-        error("Cannot set pthread cancel state to RESTORE (%d).", pthreadoldcancelstate);
-
-    return(st->usec_since_last_update);
-}
index 4189d36cd43f497b4f7cd643379a6c9b7f66f677..26623a24045382c63d066d98eed6e957cc76d72c 100644 (file)
--- a/src/rrd.h
+++ b/src/rrd.h
@@ -3,20 +3,17 @@
 
 #define UPDATE_EVERY 1
 #define UPDATE_EVERY_MAX 3600
-extern int rrd_update_every;
 
 #define RRD_DEFAULT_HISTORY_ENTRIES 3600
 #define RRD_HISTORY_ENTRIES_MAX (86400*10)
-extern int rrd_default_history_entries;
 
-// time in seconds to delete unupdated dimensions
-// set to zero to disable this feature
-extern int rrd_delete_unupdated_dimensions;
+extern int default_rrd_update_every;
+extern int default_rrd_history_entries;
 
-#define RRD_ID_LENGTH_MAX 400
+#define RRD_ID_LENGTH_MAX 200
 
-#define RRDSET_MAGIC        "NETDATA RRD SET FILE V018"
-#define RRDDIMENSION_MAGIC  "NETDATA RRD DIMENSION FILE V018"
+#define RRDSET_MAGIC        "NETDATA RRD SET FILE V019"
+#define RRDDIMENSION_MAGIC  "NETDATA RRD DIMENSION FILE V019"
 
 typedef long long total_number;
 #define TOTAL_NUMBER_FORMAT "%lld"
@@ -24,59 +21,61 @@ typedef long long total_number;
 // ----------------------------------------------------------------------------
 // chart types
 
+typedef enum rrdset_type {
+    RRDSET_TYPE_LINE    = 0,
+    RRDSET_TYPE_AREA    = 1,
+    RRDSET_TYPE_STACKED = 2
+} RRDSET_TYPE;
+
 #define RRDSET_TYPE_LINE_NAME "line"
 #define RRDSET_TYPE_AREA_NAME "area"
 #define RRDSET_TYPE_STACKED_NAME "stacked"
 
-#define RRDSET_TYPE_LINE    0
-#define RRDSET_TYPE_AREA    1
-#define RRDSET_TYPE_STACKED 2
-
-int rrdset_type_id(const char *name);
-const char *rrdset_type_name(int chart_type);
+RRDSET_TYPE rrdset_type_id(const char *name);
+const char *rrdset_type_name(RRDSET_TYPE chart_type);
 
 
 // ----------------------------------------------------------------------------
 // memory mode
 
+typedef enum rrd_memory_mode {
+    RRD_MEMORY_MODE_NONE = 0,
+    RRD_MEMORY_MODE_RAM  = 1,
+    RRD_MEMORY_MODE_MAP  = 2,
+    RRD_MEMORY_MODE_SAVE = 3
+} RRD_MEMORY_MODE;
+
+#define RRD_MEMORY_MODE_NONE_NAME "none"
 #define RRD_MEMORY_MODE_RAM_NAME "ram"
 #define RRD_MEMORY_MODE_MAP_NAME "map"
 #define RRD_MEMORY_MODE_SAVE_NAME "save"
 
-#define RRD_MEMORY_MODE_RAM 0
-#define RRD_MEMORY_MODE_MAP 1
-#define RRD_MEMORY_MODE_SAVE 2
-
-extern int rrd_memory_mode;
+extern RRD_MEMORY_MODE default_rrd_memory_mode;
 
-extern const char *rrd_memory_mode_name(int id);
-extern int rrd_memory_mode_id(const char *name);
+extern const char *rrd_memory_mode_name(RRD_MEMORY_MODE id);
+extern RRD_MEMORY_MODE rrd_memory_mode_id(const char *name);
 
 
 // ----------------------------------------------------------------------------
 // algorithms types
 
-#define RRDDIM_ABSOLUTE_NAME                "absolute"
-#define RRDDIM_INCREMENTAL_NAME             "incremental"
-#define RRDDIM_PCENT_OVER_DIFF_TOTAL_NAME   "percentage-of-incremental-row"
-#define RRDDIM_PCENT_OVER_ROW_TOTAL_NAME    "percentage-of-absolute-row"
+typedef enum rrd_algorithm {
+    RRD_ALGORITHM_ABSOLUTE              = 0,
+    RRD_ALGORITHM_INCREMENTAL           = 1,
+    RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL = 2,
+    RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL  = 3
+} RRD_ALGORITHM;
 
-#define RRDDIM_ABSOLUTE                 0
-#define RRDDIM_INCREMENTAL              1
-#define RRDDIM_PCENT_OVER_DIFF_TOTAL    2
-#define RRDDIM_PCENT_OVER_ROW_TOTAL     3
-
-extern int rrddim_algorithm_id(const char *name);
-extern const char *rrddim_algorithm_name(int chart_type);
-
-// ----------------------------------------------------------------------------
-// flags
+#define RRD_ALGORITHM_ABSOLUTE_NAME                "absolute"
+#define RRD_ALGORITHM_INCREMENTAL_NAME             "incremental"
+#define RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL_NAME   "percentage-of-incremental-row"
+#define RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL_NAME    "percentage-of-absolute-row"
 
-#define RRDDIM_FLAG_HIDDEN 0x00000001 // this dimension will not be offered to callers
-#define RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS 0x00000002 // do not offer RESET or OVERFLOW info to callers
+extern RRD_ALGORITHM rrd_algorithm_id(const char *name);
+extern const char *rrd_algorithm_name(RRD_ALGORITHM algorithm);
 
 // ----------------------------------------------------------------------------
-// RRD CONTEXT
+// RRD FAMILY
 
 struct rrdfamily {
     avl avl;
@@ -90,8 +89,25 @@ struct rrdfamily {
 };
 typedef struct rrdfamily RRDFAMILY;
 
+
+// ----------------------------------------------------------------------------
+// flags
+// use this for configuration flags, not for state control
+// flags are set/unset in a manner that is not thread safe
+// and may lead to missing information.
+
+typedef enum rrddim_flags {
+    RRDDIM_FLAG_HIDDEN                          = 1 << 0,  // this dimension will not be offered to callers
+    RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS = 1 << 1   // do not offer RESET or OVERFLOW info to callers
+} RRDDIM_FLAGS;
+
+#define rrddim_flag_check(rd, flag) ((rd)->flags & flag)
+#define rrddim_flag_set(rd, flag)   (rd)->flags |= flag
+#define rrddim_flag_clear(rd, flag) (rd)->flags &= ~flag
+
+
 // ----------------------------------------------------------------------------
-// RRD DIMENSION
+// RRD DIMENSION - this is a metric
 
 struct rrddim {
     // ------------------------------------------------------------------------
@@ -102,18 +118,20 @@ struct rrddim {
     // ------------------------------------------------------------------------
     // the dimension definition
 
-    char id[RRD_ID_LENGTH_MAX + 1];                 // the id of this dimension (for internal identification)
-
+    const char *id;                                 // the id of this dimension (for internal identification)
     const char *name;                               // the name of this dimension (as presented to user)
                                                     // this is a pointer to the config structure
                                                     // since the config always has a higher priority
                                                     // (the user overwrites the name of the charts)
+                                                    // DO NOT FREE THIS - IT IS ALLOCATED IN CONFIG
 
-    int algorithm;                                  // the algorithm that is applied to add new collected values
-    long multiplier;                                // the multiplier of the collected values
-    long divisor;                                   // the divider of the collected values
+    RRD_ALGORITHM algorithm;                        // the algorithm that is applied to add new collected values
+    RRD_MEMORY_MODE rrd_memory_mode;                // the memory mode for this dimension
 
-    int mapped;                                     // if set to non zero, this dimension is mapped to a file
+    collected_number multiplier;                    // the multiplier of the collected values
+    collected_number divisor;                       // the divider of the collected values
+
+    uint32_t flags;                                 // configuration flags for the dimension
 
     // ------------------------------------------------------------------------
     // members for temporary data we need for calculations
@@ -122,18 +140,15 @@ struct rrddim {
                                                     // instead of strcmp() every item in the binary index
                                                     // we first compare the hashes
 
-    // FIXME
-    // we need the hash_name too!
-    // needed at rrdr_disable_not_selected_dimensions()
-
-    uint32_t flags;
+    uint32_t hash_name;                             // a simple hash of the name
 
-    char cache_filename[FILENAME_MAX+1];            // the filename we load/save from/to this set
+    char *cache_filename;                           // the filename we load/save from/to this set
 
-    unsigned long counter;                          // the number of times we added values to this rrdim
+    size_t collections_counter;                     // the number of times we added values to this rrdim
+    size_t unused[10];
 
-    int updated;                                    // set to 0 after each calculation, to 1 after each collected value
-                                                    // we use this to detect that a dimension is not updated
+    int updated:1;                                  // 1 when the dimension has been updated since the last processing
+    int exposed:1;                                  // 1 when set what have sent this dimension to the central netdata
 
     struct timeval last_collected_time;             // when was this dimension last updated
                                                     // this is actual date time we updated the last_collected_value
@@ -164,7 +179,7 @@ struct rrddim {
 
     int update_every;                               // every how many seconds is this updated
 
-    unsigned long memsize;                          // the memory allocated for this dimension
+    size_t memsize;                                 // the memory allocated for this dimension
 
     char magic[sizeof(RRDDIMENSION_MAGIC) + 1];     // a string to be saved, used to identify our data file
 
@@ -177,9 +192,33 @@ struct rrddim {
 };
 typedef struct rrddim RRDDIM;
 
+// ----------------------------------------------------------------------------
+// these loop macros make sure the linked list is accessed with the right lock
+
+#define rrddim_foreach_read(rd, st) \
+    for(rd = st->dimensions, rrdset_check_rdlock(st); rd ; rd = rd->next)
+
+#define rrddim_foreach_write(rd, st) \
+    for(rd = st->dimensions, rrdset_check_wrlock(st); rd ; rd = rd->next)
+
 
 // ----------------------------------------------------------------------------
-// RRDSET
+// RRDSET - this is a chart
+
+// use this for configuration flags, not for state control
+// flags are set/unset in a manner that is not thread safe
+// and may lead to missing information.
+
+typedef enum rrdset_flags {
+    RRDSET_FLAG_ENABLED  = 1 << 0, // enables or disables a chart
+    RRDSET_FLAG_DETAIL   = 1 << 1, // if set, the data set should be considered as a detail of another
+                                   // (the master data set should be the one that has the same family and is not detail)
+    RRDSET_FLAG_DEBUG    = 1 << 2  // enables or disables debugging for a chart
+} RRDSET_FLAGS;
+
+#define rrdset_flag_check(st, flag) ((st)->flags & flag)
+#define rrdset_flag_set(st, flag)   (st)->flags |= flag
+#define rrdset_flag_clear(st, flag) (st)->flags &= ~flag
 
 struct rrdset {
     // ------------------------------------------------------------------------
@@ -198,6 +237,8 @@ struct rrdset {
                                                     // since the config always has a higher priority
                                                     // (the user overwrites the name of the charts)
 
+    char *config_section;                           // the config section for the chart
+
     char *type;                                     // the type of graph RRD_TYPE_* (a category, for determining graphing options)
     char *family;                                   // grouping sets under the same family
     char *title;                                    // title shown to user
@@ -206,7 +247,7 @@ struct rrdset {
     char *context;                                  // the template of this data set
     uint32_t hash_context;
 
-    int chart_type;
+    RRDSET_TYPE chart_type;
 
     int update_every;                               // every how many seconds is this updated?
 
@@ -215,30 +256,27 @@ struct rrdset {
     long current_entry;                             // the entry that is currently being updated
                                                     // it goes around in a round-robin fashion
 
-    int enabled;
+    uint32_t flags;                                 // configuration flags
 
     int gap_when_lost_iterations_above;             // after how many lost iterations a gap should be stored
                                                     // netdata will interpolate values for gaps lower than this
 
     long priority;
 
-    int isdetail;                                   // if set, the data set should be considered as a detail of another
-                                                    // (the master data set should be the one that has the same family and is not detail)
 
     // ------------------------------------------------------------------------
     // members for temporary data we need for calculations
 
-    int mapped;                                     // if set to 1, this is memory mapped
-
-    int debug;
+    RRD_MEMORY_MODE rrd_memory_mode;                // if set to 1, this is memory mapped
 
     char *cache_dir;                                // the directory to store dimensions
     char cache_filename[FILENAME_MAX+1];            // the filename to store this set
 
-    pthread_rwlock_t rwlock;
+    pthread_rwlock_t rrdset_rwlock;                 // protects dimensions linked list
 
-    unsigned long counter;                          // the number of times we added values to this rrd
-    unsigned long counter_done;                     // the number of times we added values to this rrd
+    size_t counter;                                 // the number of times we added values to this database
+    size_t counter_done;                            // the number of times rrdset_done() has been called
+    size_t unused[10];
 
     uint32_t hash;                                  // a simple hash on the id, to speed up searching
                                                     // we first compare hashes, and only if the hashes are equal we do string comparisons
@@ -284,83 +322,223 @@ struct rrdset {
 };
 typedef struct rrdset RRDSET;
 
+#define rrdset_rdlock(st) pthread_rwlock_rdlock(&((st)->rrdset_rwlock))
+#define rrdset_wrlock(st) pthread_rwlock_wrlock(&((st)->rrdset_rwlock))
+#define rrdset_unlock(st) pthread_rwlock_unlock(&((st)->rrdset_rwlock))
+
+// ----------------------------------------------------------------------------
+// these loop macros make sure the linked list is accessed with the right lock
+
+#define rrdset_foreach_read(st, host) \
+    for(st = host->rrdset_root, rrdhost_check_rdlock(host); st ; st = st->next)
+
+#define rrdset_foreach_write(st, host) \
+    for(st = host->rrdset_root, rrdhost_check_wrlock(host); st ; st = st->next)
+
+
 // ----------------------------------------------------------------------------
 // RRD HOST
 
 struct rrdhost {
-    avl avl;
+    avl avl;                                        // the index of hosts
+
+    // ------------------------------------------------------------------------
+    // host information
 
-    char *hostname;
+    char *hostname;                                 // the hostname of this host
+    uint32_t hash_hostname;                         // the hostname hash
 
-    RRDSET *rrdset_root;
-    pthread_rwlock_t rrdset_root_rwlock;
+    char machine_guid[GUID_LEN + 1];                // the unique ID of this host
+    uint32_t hash_machine_guid;                     // the hash of the unique ID
 
-    avl_tree_lock rrdset_root_index;
-    avl_tree_lock rrdset_root_index_name;
+    char *os;                                       // the O/S type of the host
+    int rrd_update_every;                           // the update frequency of the host
+    int rrd_history_entries;                        // the number of history entries for the host's charts
+    RRD_MEMORY_MODE rrd_memory_mode;                // the memory more for the charts of this host
+
+    char *cache_dir;                                // the directory to save RRD cache files
+    char *varlib_dir;                               // the directory to save health log
+
+
+    // ------------------------------------------------------------------------
+    // streaming of data to remote hosts - rrdpush
+
+    int rrdpush_enabled:1;                          // 1 when this host sends metrics to another netdata
+    char *rrdpush_destination;                      // where to send metrics to
+    char *rrdpush_api_key;                          // the api key at the receiving netdata
+    volatile int rrdpush_connected:1;               // 1 when the sender is ready to push metrics
+    volatile int rrdpush_spawn:1;                   // 1 when the sender thread has been spawn
+    volatile int rrdpush_error_shown:1;             // 1 when we have logged a communication error
+    int rrdpush_socket;                             // the fd of the socket to the remote host, or -1
+    pthread_t rrdpush_thread;                       // the sender thread
+    pthread_mutex_t rrdpush_mutex;                  // exclusive access to rrdpush_buffer
+    int rrdpush_pipe[2];                            // collector to sender thread communication
+    BUFFER *rrdpush_buffer;                         // collector fills it, sender sends them
 
-    avl_tree_lock rrdfamily_root_index;
-    avl_tree_lock variables_root_index;
+
+    // ------------------------------------------------------------------------
+    // streaming of data from remote hosts - rrdpush
+
+    volatile size_t connected_senders;              // when remote hosts are streaming to this
+                                                    // host, this is the counter of connected clients
+
+    time_t senders_disconnected_time;               // the time the last sender was disconnected
+
+    // ------------------------------------------------------------------------
+    // health monitoring options
+
+    int health_enabled:1;                           // 1 when this host has health enabled
+    time_t health_delay_up_to;                      // a timestamp to delay alarms processing up to
+    char *health_default_exec;                      // the full path of the alarms notifications program
+    char *health_default_recipient;                 // the default recipient for all alarms
+    char *health_log_filename;                      // the alarms event log filename
+    size_t health_log_entries_written;              // the number of alarm events writtern to the alarms event log
+    FILE *health_log_fp;                            // the FILE pointer to the open alarms event log file
 
     // all RRDCALCs are primarily allocated and linked here
     // RRDCALCs may be linked to charts at any point
     // (charts may or may not exist when these are loaded)
     RRDCALC *alarms;
-    ALARM_LOG health_log;
 
+    ALARM_LOG health_log;                           // alarms historical events (event log)
+
+    // templates of alarms
+    // these are used to create alarms when charts
+    // are created or renamed, that match them
     RRDCALCTEMPLATE *templates;
+
+
+    // ------------------------------------------------------------------------
+    // the charts of the host
+
+    RRDSET *rrdset_root;                            // the host charts
+
+
+    // ------------------------------------------------------------------------
+    // locks
+
+    pthread_rwlock_t rrdhost_rwlock;                // lock for this RRDHOST (protects rrdset_root linked list)
+
+    avl_tree_lock rrdset_root_index;                // the host's charts index (by id)
+    avl_tree_lock rrdset_root_index_name;           // the host's charts index (by name)
+
+    avl_tree_lock rrdfamily_root_index;             // the host's chart families index
+    avl_tree_lock variables_root_index;             // the host's chart variables index
+
+    struct rrdhost *next;
 };
 typedef struct rrdhost RRDHOST;
-extern RRDHOST localhost;
-extern void rrdhost_init(char *hostname);
+extern RRDHOST *localhost;
+
+#define rrdhost_rdlock(h) pthread_rwlock_rdlock(&((h)->rrdhost_rwlock))
+#define rrdhost_wrlock(h) pthread_rwlock_wrlock(&((h)->rrdhost_rwlock))
+#define rrdhost_unlock(h) pthread_rwlock_unlock(&((h)->rrdhost_rwlock))
+
+// ----------------------------------------------------------------------------
+// these loop macros make sure the linked list is accessed with the right lock
+
+#define rrdhost_foreach_read(var) \
+    for(var = localhost, rrd_check_rdlock(); var ; var = var->next)
+
+#define rrdhost_foreach_write(var) \
+    for(var = localhost, rrd_check_wrlock(); var ; var = var->next)
+
+
+// ----------------------------------------------------------------------------
+// global lock for all RRDHOSTs
+
+extern pthread_rwlock_t rrd_rwlock;
+#define rrd_rdlock() pthread_rwlock_rdlock(&rrd_rwlock)
+#define rrd_wrlock() pthread_rwlock_wrlock(&rrd_rwlock)
+#define rrd_unlock() pthread_rwlock_unlock(&rrd_rwlock)
+
+// ----------------------------------------------------------------------------
+
+extern size_t rrd_hosts_available;
+extern time_t rrdhost_free_orphan_time;
+
+extern void rrd_init(char *hostname);
+
+extern RRDHOST *rrdhost_find_by_hostname(const char *hostname, uint32_t hash);
+extern RRDHOST *rrdhost_find_by_guid(const char *guid, uint32_t hash);
+
+extern RRDHOST *rrdhost_find_or_create(
+        const char *hostname
+        , const char *guid
+        , const char *os
+        , int update_every
+        , int history
+        , RRD_MEMORY_MODE mode
+        , int health_enabled
+        , int rrdpush_enabled
+        , char *rrdpush_destination
+        , char *rrdpush_api_key
+);
 
 #ifdef NETDATA_INTERNAL_CHECKS
-#define rrdhost_check_wrlock(host) rrdhost_check_wrlock_int(host, __FILE__, __FUNCTION__, __LINE__)
+extern void rrdhost_check_wrlock_int(RRDHOST *host, const char *file, const char *function, const unsigned long line);
+extern void rrdhost_check_rdlock_int(RRDHOST *host, const char *file, const char *function, const unsigned long line);
+extern void rrdset_check_rdlock_int(RRDSET *st, const char *file, const char *function, const unsigned long line);
+extern void rrdset_check_wrlock_int(RRDSET *st, const char *file, const char *function, const unsigned long line);
+extern void rrd_check_rdlock_int(const char *file, const char *function, const unsigned long line);
+extern void rrd_check_wrlock_int(const char *file, const char *function, const unsigned long line);
+
 #define rrdhost_check_rdlock(host) rrdhost_check_rdlock_int(host, __FILE__, __FUNCTION__, __LINE__)
+#define rrdhost_check_wrlock(host) rrdhost_check_wrlock_int(host, __FILE__, __FUNCTION__, __LINE__)
+#define rrdset_check_rdlock(st) rrdset_check_rdlock_int(st, __FILE__, __FUNCTION__, __LINE__)
+#define rrdset_check_wrlock(st) rrdset_check_wrlock_int(st, __FILE__, __FUNCTION__, __LINE__)
+#define rrd_check_rdlock() rrd_check_rdlock_int(__FILE__, __FUNCTION__, __LINE__)
+#define rrd_check_wrlock() rrd_check_wrlock_int(__FILE__, __FUNCTION__, __LINE__)
+
 #else
 #define rrdhost_check_rdlock(host) (void)0
 #define rrdhost_check_wrlock(host) (void)0
+#define rrdset_check_rdlock(host) (void)0
+#define rrdset_check_wrlock(host) (void)0
+#define rrd_check_rdlock() (void)0
+#define rrd_check_wrlock() (void)0
 #endif
 
-extern void rrdhost_check_wrlock_int(RRDHOST *host, const char *file, const char *function, const unsigned long line);
-extern void rrdhost_check_rdlock_int(RRDHOST *host, const char *file, const char *function, const unsigned long line);
-
-extern void rrdhost_rwlock(RRDHOST *host);
-extern void rrdhost_rdlock(RRDHOST *host);
-extern void rrdhost_unlock(RRDHOST *host);
-
 // ----------------------------------------------------------------------------
-// RRD SET functions
+// RRDSET functions
 
-extern char *rrdset_strncpyz_name(char *to, const char *from, size_t length);
 extern void rrdset_set_name(RRDSET *st, const char *name);
 
-extern char *rrdset_cache_dir(const char *id);
+extern RRDSET *rrdset_create(RRDHOST *host
+                             , const char *type
+                             , const char *id
+                             , const char *name
+                             , const char *family
+                             , const char *context
+                             , const char *title
+                             , const char *units
+                             , long priority
+                             , int update_every
+                             , RRDSET_TYPE chart_type);
 
-extern void rrdset_reset(RRDSET *st);
+#define rrdset_create_localhost(type, id, name, family, context, title, units, priority, update_every, chart_type) rrdset_create(localhost, type, id, name, family, context, title, units, priority, update_every, chart_type)
 
-extern RRDSET *rrdset_create(const char *type
-        , const char *id
-        , const char *name
-        , const char *family
-        , const char *context
-        , const char *title
-        , const char *units
-        , long priority
-        , int update_every
-        , int chart_type);
+extern void rrdhost_free_all(void);
+extern void rrdhost_save_all(void);
+
+extern void rrdhost_cleanup_remote_stale(RRDHOST *protected);
+extern void rrdhost_free(RRDHOST *host);
+extern void rrdhost_save(RRDHOST *host);
 
-extern void rrdset_free_all(void);
-extern void rrdset_save_all(void);
+extern RRDSET *rrdset_find(RRDHOST *host, const char *id);
+#define rrdset_find_localhost(id) rrdset_find(localhost, id)
 
-extern RRDSET *rrdset_find(const char *id);
-extern RRDSET *rrdset_find_bytype(const char *type, const char *id);
-extern RRDSET *rrdset_find_byname(const char *name);
+extern RRDSET *rrdset_find_bytype(RRDHOST *host, const char *type, const char *id);
+#define rrdset_find_bytype_localhost(type, id) rrdset_find_bytype(localhost, type, id)
+
+extern RRDSET *rrdset_find_byname(RRDHOST *host, const char *name);
+#define rrdset_find_byname_localhost(name)  rrdset_find_byname(localhost, name)
 
 extern void rrdset_next_usec_unfiltered(RRDSET *st, usec_t microseconds);
 extern void rrdset_next_usec(RRDSET *st, usec_t microseconds);
 #define rrdset_next(st) rrdset_next_usec(st, 0ULL)
 
-extern usec_t rrdset_done(RRDSET *st);
+extern void rrdset_done(RRDSET *st);
 
 // get the total duration in seconds of the round robin database
 #define rrdset_duration(st) ((time_t)( (((st)->counter >= ((unsigned long)(st)->entries))?(unsigned long)(st)->entries:(st)->counter) * (st)->update_every ))
@@ -398,11 +576,9 @@ extern usec_t rrdset_done(RRDSET *st);
 // ----------------------------------------------------------------------------
 // RRD DIMENSION functions
 
-extern RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, long multiplier, long divisor, int algorithm);
+extern RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, collected_number multiplier, collected_number divisor, RRD_ALGORITHM algorithm);
 
 extern void rrddim_set_name(RRDSET *st, RRDDIM *rd, const char *name);
-extern void rrddim_free(RRDSET *st, RRDDIM *rd);
-
 extern RRDDIM *rrddim_find(RRDSET *st, const char *id);
 
 extern int rrddim_hide(RRDSET *st, const char *id);
@@ -411,4 +587,37 @@ extern int rrddim_unhide(RRDSET *st, const char *id);
 extern collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value);
 extern collected_number rrddim_set(RRDSET *st, const char *id, collected_number value);
 
+extern long align_entries_to_pagesize(RRD_MEMORY_MODE mode, long entries);
+
+
+// ----------------------------------------------------------------------------
+// RRD internal functions
+
+#ifdef NETDATA_RRD_INTERNALS
+
+extern void rrdset_free(RRDSET *st);
+extern avl_tree_lock rrdhost_root_index;
+
+extern char *rrdset_strncpyz_name(char *to, const char *from, size_t length);
+extern char *rrdset_cache_dir(RRDHOST *host, const char *id, const char *config_section);
+
+extern void rrdset_reset(RRDSET *st);
+
+extern void rrddim_free(RRDSET *st, RRDDIM *rd);
+
+extern int rrddim_compare(void* a, void* b);
+extern int rrdset_compare(void* a, void* b);
+extern int rrdset_compare_name(void* a, void* b);
+extern int rrdfamily_compare(void *a, void *b);
+
+extern RRDFAMILY *rrdfamily_create(RRDHOST *host, const char *id);
+extern void rrdfamily_free(RRDHOST *host, RRDFAMILY *rc);
+
+#define rrdset_index_add(host, st) (RRDSET *)avl_insert_lock(&((host)->rrdset_root_index), (avl *)(st))
+#define rrdset_index_del(host, st) (RRDSET *)avl_remove_lock(&((host)->rrdset_root_index), (avl *)(st))
+extern RRDSET *rrdset_index_del_name(RRDHOST *host, RRDSET *st);
+
+#endif /* NETDATA_RRD_INTERNALS */
+
+
 #endif /* NETDATA_RRD_H */
index b886b25fd29954cc695c05349bc51fc6e307de2c..2a46fb134568721be224599c6168fac301d20d0d 100644 (file)
@@ -2,7 +2,7 @@
 
 void rrd_stats_api_v1_chart_with_data(RRDSET *st, BUFFER *wb, size_t *dimensions_count, size_t *memory_used)
 {
-    pthread_rwlock_rdlock(&st->rwlock);
+    rrdset_rdlock(st);
 
     buffer_sprintf(wb,
         "\t\t{\n"
@@ -29,7 +29,7 @@ void rrd_stats_api_v1_chart_with_data(RRDSET *st, BUFFER *wb, size_t *dimensions
         , st->context
         , st->title
         , st->priority
-        , st->enabled?"true":"false"
+        , rrdset_flag_check(st, RRDSET_FLAG_ENABLED)?"true":"false"
         , st->units
         , st->name
         , rrdset_type_name(st->chart_type)
@@ -43,8 +43,8 @@ void rrd_stats_api_v1_chart_with_data(RRDSET *st, BUFFER *wb, size_t *dimensions
 
     size_t dimensions = 0;
     RRDDIM *rd;
-    for(rd = st->dimensions; rd ; rd = rd->next) {
-        if(rd->flags & RRDDIM_FLAG_HIDDEN) continue;
+    rrddim_foreach_read(rd, st) {
+        if(rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)) continue;
 
         memory += rd->memsize;
 
@@ -71,14 +71,14 @@ void rrd_stats_api_v1_chart_with_data(RRDSET *st, BUFFER *wb, size_t *dimensions
         "\n\t\t}"
         );
 
-    pthread_rwlock_unlock(&st->rwlock);
+    rrdset_unlock(st);
 }
 
 void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb) {
     rrd_stats_api_v1_chart_with_data(st, wb, NULL, NULL);
 }
 
-void rrd_stats_api_v1_charts(BUFFER *wb)
+void rrd_stats_api_v1_charts(RRDHOST *host, BUFFER *wb)
 {
     size_t c, dimensions = 0, memory = 0, alarms = 0;
     RRDSET *st;
@@ -90,16 +90,17 @@ void rrd_stats_api_v1_charts(BUFFER *wb)
         ",\n\t\"update_every\": %d"
         ",\n\t\"history\": %d"
         ",\n\t\"charts\": {"
-        , localhost.hostname
+        , host->hostname
         , program_version
-        , os_type
-        , rrd_update_every
-        , rrd_default_history_entries
+        , host->os
+        , host->rrd_update_every
+        , host->rrd_history_entries
         );
 
-    pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock);
-    for(st = localhost.rrdset_root, c = 0; st ; st = st->next) {
-        if(st->enabled && st->dimensions) {
+    c = 0;
+    rrdhost_rdlock(host);
+    rrdset_foreach_read(st, host) {
+        if(rrdset_flag_check(st, RRDSET_FLAG_ENABLED) && st->dimensions) {
             if(c) buffer_strcat(wb, ",");
             buffer_strcat(wb, "\n\t\t\"");
             buffer_strcat(wb, st->id);
@@ -110,23 +111,49 @@ void rrd_stats_api_v1_charts(BUFFER *wb)
     }
 
     RRDCALC *rc;
-    for(rc = localhost.alarms; rc ; rc = rc->next) {
+    for(rc = host->alarms; rc ; rc = rc->next) {
         if(rc->rrdset)
             alarms++;
     }
-    pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
+    rrdhost_unlock(host);
 
     buffer_sprintf(wb, "\n\t}"
                     ",\n\t\"charts_count\": %zu"
                     ",\n\t\"dimensions_count\": %zu"
                     ",\n\t\"alarms_count\": %zu"
                     ",\n\t\"rrd_memory_bytes\": %zu"
-                    "\n}\n"
+                    ",\n\t\"hosts_count\": %zu"
+                    ",\n\t\"hosts\": ["
                    , c
                    , dimensions
                    , alarms
                    , memory
+                   , rrd_hosts_available
     );
+
+    if(unlikely(rrd_hosts_available > 1)) {
+        rrd_rdlock();
+        RRDHOST *h;
+        rrdhost_foreach_read(h)
+            buffer_sprintf(wb,
+                    "%s\n\t\t{"
+                    "\n\t\t\t\"hostname\": \"%s\""
+                    "\n\t\t}"
+                    , (h != localhost)?",":""
+                    , h->hostname
+            );
+        rrd_unlock();
+    }
+    else {
+        buffer_sprintf(wb,
+                "\n\t\t{"
+                "\n\t\t\t\"hostname\": \"%s\""
+                "\n\t\t}"
+                , host->hostname
+        );
+    }
+
+    buffer_sprintf(wb, "\n\t]\n}\n");
 }
 
 // ----------------------------------------------------------------------------
@@ -149,35 +176,34 @@ static inline size_t prometheus_name_copy(char *d, const char *s, size_t usable)
 
 #define PROMETHEUS_ELEMENT_MAX 256
 
-void rrd_stats_api_v1_charts_allmetrics_prometheus(BUFFER *wb)
-{
-    pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock);
+void rrd_stats_api_v1_charts_allmetrics_prometheus(RRDHOST *host, BUFFER *wb) {
+    rrdhost_rdlock(host);
 
-    char host[PROMETHEUS_ELEMENT_MAX + 1];
-    prometheus_name_copy(host, config_get("global", "hostname", "localhost"), PROMETHEUS_ELEMENT_MAX);
+    char hostname[PROMETHEUS_ELEMENT_MAX + 1];
+    prometheus_name_copy(hostname, host->hostname, PROMETHEUS_ELEMENT_MAX);
 
     // for each chart
     RRDSET *st;
-    for(st = localhost.rrdset_root; st ; st = st->next) {
+    rrdset_foreach_read(st, host) {
         char chart[PROMETHEUS_ELEMENT_MAX + 1];
         prometheus_name_copy(chart, st->id, PROMETHEUS_ELEMENT_MAX);
 
         buffer_strcat(wb, "\n");
-        if(st->enabled && st->dimensions) {
-            pthread_rwlock_rdlock(&st->rwlock);
+        if(rrdset_flag_check(st, RRDSET_FLAG_ENABLED) && st->dimensions) {
+            rrdset_rdlock(st);
 
             // for each dimension
             RRDDIM *rd;
-            for(rd = st->dimensions; rd ; rd = rd->next) {
-                if(rd->counter) {
+            rrddim_foreach_read(rd, st) {
+                if(rd->collections_counter) {
                     char dimension[PROMETHEUS_ELEMENT_MAX + 1];
                     prometheus_name_copy(dimension, rd->id, PROMETHEUS_ELEMENT_MAX);
 
                     // buffer_sprintf(wb, "# HELP %s.%s %s\n", st->id, rd->id, st->units);
 
                     switch(rd->algorithm) {
-                        case RRDDIM_INCREMENTAL:
-                        case RRDDIM_PCENT_OVER_DIFF_TOTAL:
+                        case RRD_ALGORITHM_INCREMENTAL:
+                        case RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL:
                             buffer_sprintf(wb, "# TYPE %s_%s counter\n", chart, dimension);
                             break;
 
@@ -191,17 +217,17 @@ void rrd_stats_api_v1_charts_allmetrics_prometheus(BUFFER *wb)
                     //        (unsigned long long)((rd->last_collected_time.tv_sec * 1000) + (rd->last_collected_time.tv_usec / 1000)));
 
                     buffer_sprintf(wb, "%s_%s{instance=\"%s\"} " COLLECTED_NUMBER_FORMAT " %llu\n",
-                            chart, dimension, host, rd->last_collected_value,
+                            chart, dimension, hostname, rd->last_collected_value,
                             (unsigned long long)((rd->last_collected_time.tv_sec * 1000) + (rd->last_collected_time.tv_usec / 1000)));
 
                 }
             }
 
-            pthread_rwlock_unlock(&st->rwlock);
+            rrdset_unlock(st);
         }
     }
 
-    pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
+    rrdhost_unlock(host);
 }
 
 // ----------------------------------------------------------------------------
@@ -224,28 +250,24 @@ static inline size_t shell_name_copy(char *d, const char *s, size_t usable) {
 
 #define SHELL_ELEMENT_MAX 100
 
-void rrd_stats_api_v1_charts_allmetrics_shell(BUFFER *wb)
-{
-    pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock);
-
-    char host[SHELL_ELEMENT_MAX + 1];
-    shell_name_copy(host, config_get("global", "hostname", "localhost"), SHELL_ELEMENT_MAX);
+void rrd_stats_api_v1_charts_allmetrics_shell(RRDHOST *host, BUFFER *wb) {
+    rrdhost_rdlock(host);
 
     // for each chart
     RRDSET *st;
-    for(st = localhost.rrdset_root; st ; st = st->next) {
+    rrdset_foreach_read(st, host) {
         calculated_number total = 0.0;
         char chart[SHELL_ELEMENT_MAX + 1];
         shell_name_copy(chart, st->id, SHELL_ELEMENT_MAX);
 
         buffer_sprintf(wb, "\n# chart: %s (name: %s)\n", st->id, st->name);
-        if(st->enabled && st->dimensions) {
-            pthread_rwlock_rdlock(&st->rwlock);
+        if(rrdset_flag_check(st, RRDSET_FLAG_ENABLED) && st->dimensions) {
+            rrdset_rdlock(st);
 
             // for each dimension
             RRDDIM *rd;
-            for(rd = st->dimensions; rd ; rd = rd->next) {
-                if(rd->counter) {
+            rrddim_foreach_read(rd, st) {
+                if(rd->collections_counter) {
                     char dimension[SHELL_ELEMENT_MAX + 1];
                     shell_name_copy(dimension, rd->id, SHELL_ELEMENT_MAX);
 
@@ -256,7 +278,7 @@ void rrd_stats_api_v1_charts_allmetrics_shell(BUFFER *wb)
                     else {
                         if(rd->multiplier < 0 || rd->divisor < 0) n = -n;
                         n = roundl(n);
-                        if(!(rd->flags & RRDDIM_FLAG_HIDDEN)) total += n;
+                        if(!rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)) total += n;
                         buffer_sprintf(wb, "NETDATA_%s_%s=\"%0.0Lf\"      # %s\n", chart, dimension, n, st->units);
                     }
                 }
@@ -264,14 +286,14 @@ void rrd_stats_api_v1_charts_allmetrics_shell(BUFFER *wb)
 
             total = roundl(total);
             buffer_sprintf(wb, "NETDATA_%s_VISIBLETOTAL=\"%0.0Lf\"      # %s\n", chart, total, st->units);
-            pthread_rwlock_unlock(&st->rwlock);
+            rrdset_unlock(st);
         }
     }
 
     buffer_strcat(wb, "\n# NETDATA ALARMS RUNNING\n");
 
     RRDCALC *rc;
-    for(rc = localhost.alarms; rc ;rc = rc->next) {
+    for(rc = host->alarms; rc ;rc = rc->next) {
         if(!rc->rrdset) continue;
 
         char chart[SHELL_ELEMENT_MAX + 1];
@@ -292,7 +314,7 @@ void rrd_stats_api_v1_charts_allmetrics_shell(BUFFER *wb)
         buffer_sprintf(wb, "NETDATA_ALARM_%s_%s_STATUS=\"%s\"\n", chart, alarm, rrdcalc_status2string(rc->status));
     }
 
-    pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
+    rrdhost_unlock(host);
 }
 
 // ----------------------------------------------------------------------------
@@ -301,7 +323,7 @@ unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb)
 {
     time_t now = now_realtime_sec();
 
-    pthread_rwlock_rdlock(&st->rwlock);
+    rrdset_rdlock(st);
 
     buffer_sprintf(wb,
         "\t\t{\n"
@@ -335,7 +357,7 @@ unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb)
         , st->context
         , st->title
         , st->priority
-        , st->enabled
+        , rrdset_flag_check(st, RRDSET_FLAG_ENABLED)?1:0
         , st->units
         , st->name, options?options:""
         , rrdset_type_name(st->chart_type)
@@ -346,7 +368,7 @@ unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb)
         , rrdset_last_entry_t(st)
         , (now < rrdset_last_entry_t(st)) ? (time_t)0 : now - rrdset_last_entry_t(st)
         , st->update_every
-        , st->isdetail
+        , rrdset_flag_check(st, RRDSET_FLAG_DETAIL)?1:0
         , st->usec_since_last_update
         , st->collected_total
         , st->last_collected_total
@@ -355,7 +377,7 @@ unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb)
     unsigned long memory = st->memsize;
 
     RRDDIM *rd;
-    for(rd = st->dimensions; rd ; rd = rd->next) {
+    rrddim_foreach_read(rd, st) {
 
         memory += rd->memsize;
 
@@ -366,8 +388,8 @@ unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb)
             "\t\t\t\t\t\"entries\": %ld,\n"
             "\t\t\t\t\t\"isHidden\": %d,\n"
             "\t\t\t\t\t\"algorithm\": \"%s\",\n"
-            "\t\t\t\t\t\"multiplier\": %ld,\n"
-            "\t\t\t\t\t\"divisor\": %ld,\n"
+            "\t\t\t\t\t\"multiplier\": " COLLECTED_NUMBER_FORMAT ",\n"
+            "\t\t\t\t\t\"divisor\": " COLLECTED_NUMBER_FORMAT ",\n"
             "\t\t\t\t\t\"last_entry_t\": %ld,\n"
             "\t\t\t\t\t\"collected_value\": " COLLECTED_NUMBER_FORMAT ",\n"
             "\t\t\t\t\t\"calculated_value\": " CALCULATED_NUMBER_FORMAT ",\n"
@@ -378,8 +400,8 @@ unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb)
             , rd->id
             , rd->name
             , rd->entries
-            , (rd->flags & RRDDIM_FLAG_HIDDEN)?1:0
-            , rrddim_algorithm_name(rd->algorithm)
+            , rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)?1:0
+            , rrd_algorithm_name(rd->algorithm)
             , rd->multiplier
             , rd->divisor
             , rd->last_collected_time.tv_sec
@@ -399,7 +421,7 @@ unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb)
         , memory
         );
 
-    pthread_rwlock_unlock(&st->rwlock);
+    rrdset_unlock(st);
     return memory;
 }
 
@@ -413,23 +435,23 @@ void rrd_stats_graph_json(RRDSET *st, char *options, BUFFER *wb)
     buffer_strcat(wb, RRD_GRAPH_JSON_FOOTER);
 }
 
-void rrd_stats_all_json(BUFFER *wb)
+void rrd_stats_all_json(RRDHOST *host, BUFFER *wb)
 {
     unsigned long memory = 0;
-    long c;
+    long c = 0;
     RRDSET *st;
 
     buffer_strcat(wb, RRD_GRAPH_JSON_HEADER);
 
-    pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock);
-    for(st = localhost.rrdset_root, c = 0; st ; st = st->next) {
-        if(st->enabled && st->dimensions) {
+    rrdhost_rdlock(host);
+    rrdset_foreach_read(st, host) {
+        if(rrdset_flag_check(st, RRDSET_FLAG_ENABLED) && st->dimensions) {
             if(c) buffer_strcat(wb, ",\n");
             memory += rrd_stats_one_json(st, NULL, wb);
             c++;
         }
     }
-    pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
+    rrdhost_unlock(host);
 
     buffer_sprintf(wb, "\n\t],\n"
         "\t\"hostname\": \"%s\",\n"
@@ -437,9 +459,9 @@ void rrd_stats_all_json(BUFFER *wb)
         "\t\"history\": %d,\n"
         "\t\"memory\": %lu\n"
         "}\n"
-        , localhost.hostname
-        , rrd_update_every
-        , rrd_default_history_entries
+        , host->hostname
+        , host->rrd_update_every
+        , host->rrd_history_entries
         , memory
         );
 }
@@ -546,8 +568,9 @@ static void rrdr_dump(RRDR *r)
 }
 */
 
-void rrdr_disable_not_selected_dimensions(RRDR *r, uint32_t options, const char *dims)
-{
+void rrdr_disable_not_selected_dimensions(RRDR *r, uint32_t options, const char *dims) {
+    rrdset_check_rdlock(r->st);
+
     if(unlikely(!dims || !*dims)) return;
 
     char b[strlen(dims) + 1];
@@ -568,7 +591,7 @@ void rrdr_disable_not_selected_dimensions(RRDR *r, uint32_t options, const char
 
         // find it and enable it
         for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
-            if(unlikely((hash == d->hash && !strcmp(d->id, tok)) || !strcmp(d->name, tok))) {
+            if(unlikely((hash == d->hash && !strcmp(d->id, tok)) || (hash == d->hash_name && !strcmp(d->name, tok)))) {
 
                 if(likely(r->od[c] & RRDR_HIDDEN)) {
                     r->od[c] |= RRDR_SELECTED;
@@ -653,6 +676,8 @@ void rrdr_buffer_print_format(BUFFER *wb, uint32_t format)
 
 uint32_t rrdr_check_options(RRDR *r, uint32_t options, const char *dims)
 {
+    rrdset_check_rdlock(r->st);
+
     (void)dims;
 
     if(options & RRDR_OPTION_NONZERO) {
@@ -688,6 +713,8 @@ uint32_t rrdr_check_options(RRDR *r, uint32_t options, const char *dims)
 
 void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value)
 {
+    rrdset_check_rdlock(r->st);
+
     long rows = rrdr_rows(r);
     long c, i;
     RRDDIM *rd;
@@ -869,6 +896,8 @@ void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, uint32_t format, uint32_t option
 
 static void rrdr2json(RRDR *r, BUFFER *wb, uint32_t options, int datatable)
 {
+    rrdset_check_rdlock(r->st);
+
     //info("RRD2JSON(): %s: BEGIN", r->st->id);
     int row_annotations = 0, dates, dates_with_new = 0;
     char kq[2] = "",                    // key quote
@@ -1095,6 +1124,8 @@ static void rrdr2json(RRDR *r, BUFFER *wb, uint32_t options, int datatable)
 
 static void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t options, const char *startline, const char *separator, const char *endline, const char *betweenlines)
 {
+    rrdset_check_rdlock(r->st);
+
     //info("RRD2CSV(): %s: BEGIN", r->st->id);
     long c, i;
     RRDDIM *d;
@@ -1200,6 +1231,8 @@ static void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t options, const char *startlin
 }
 
 inline static calculated_number rrdr2value(RRDR *r, long i, uint32_t options, int *all_values_are_null) {
+    rrdset_check_rdlock(r->st);
+
     long c;
     RRDDIM *d;
 
@@ -1350,7 +1383,7 @@ inline static void rrdr_lock_rrdset(RRDR *r) {
         return;
     }
 
-    pthread_rwlock_rdlock(&r->st->rwlock);
+    rrdset_rdlock(r->st);
     r->has_st_lock = 1;
 }
 
@@ -1361,7 +1394,7 @@ inline static void rrdr_unlock_rrdset(RRDR *r) {
     }
 
     if(likely(r->has_st_lock)) {
-        pthread_rwlock_unlock(&r->st->rwlock);
+        rrdset_unlock(r->st);
         r->has_st_lock = 0;
     }
 }
@@ -1400,7 +1433,7 @@ static RRDR *rrdr_create(RRDSET *st, long n)
     rrdr_lock_rrdset(r);
 
     RRDDIM *rd;
-    for(rd = st->dimensions ; rd ; rd = rd->next) r->d++;
+    rrddim_foreach_read(rd, st) r->d++;
 
     r->n = n;
 
@@ -1412,8 +1445,10 @@ static RRDR *rrdr_create(RRDSET *st, long n)
     // set the hidden flag on hidden dimensions
     int c;
     for(c = 0, rd = st->dimensions ; rd ; c++, rd = rd->next) {
-        if(unlikely(rd->flags & RRDDIM_FLAG_HIDDEN)) r->od[c] = RRDR_HIDDEN;
-        else r->od[c] = 0;
+        if(unlikely(rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)))
+            r->od[c] = RRDR_HIDDEN;
+        else
+            r->od[c] = 0;
     }
 
     r->c = -1;
@@ -1425,7 +1460,7 @@ static RRDR *rrdr_create(RRDSET *st, long n)
 
 RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int group_method, int aligned)
 {
-    int debug = st->debug;
+    int debug = rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?1:0;
     int absolute_period_requested = -1;
 
     time_t first_entry_t = rrdset_first_entry_t(st);
@@ -1601,6 +1636,7 @@ RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int g
     // initialize them
     RRDDIM *rd;
     long c;
+    rrdset_check_rdlock(st);
     for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
         last_values[c] = 0;
         group_values[c] = (group_method == GROUP_MAX || group_method == GROUP_MIN)?NAN:0;
@@ -1996,7 +2032,7 @@ int rrd2format(RRDSET *st, BUFFER *wb, BUFFER *dimensions, uint32_t format, long
 time_t rrd_stats_json(int type, RRDSET *st, BUFFER *wb, long points, long group, int group_method, time_t after, time_t before, int only_non_zero)
 {
     int c;
-    pthread_rwlock_rdlock(&st->rwlock);
+    rrdset_rdlock(st);
 
 
     // -------------------------------------------------------------------------
@@ -2038,9 +2074,9 @@ time_t rrd_stats_json(int type, RRDSET *st, BUFFER *wb, long points, long group,
 
     int dimensions = 0;
     RRDDIM *rd;
-    for( rd = st->dimensions ; rd ; rd = rd->next) dimensions++;
+    rrddim_foreach_read(rd, st) dimensions++;
     if(!dimensions) {
-        pthread_rwlock_unlock(&st->rwlock);
+        rrdset_unlock(st);
         buffer_strcat(wb, "No dimensions yet.");
         return 0;
     }
@@ -2060,7 +2096,7 @@ time_t rrd_stats_json(int type, RRDSET *st, BUFFER *wb, long points, long group,
     // -------------------------------------------------------------------------
     // checks for debugging
 
-    if(st->debug) {
+    if(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)) {
         debug(D_RRD_STATS, "%s first_entry_t = %ld, last_entry_t = %ld, duration = %ld, after = %ld, before = %ld, duration = %ld, entries_to_show = %ld, group = %ld"
             , st->id
             , rrdset_first_entry_t(st)
@@ -2092,7 +2128,7 @@ time_t rrd_stats_json(int type, RRDSET *st, BUFFER *wb, long points, long group,
     // initialize them
     for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
         group_values[c] = 0;
-        print_hidden[c] = (rd->flags & RRDDIM_FLAG_HIDDEN)?1:0;
+        print_hidden[c] = rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)?1:0;
         found_non_zero[c] = 0;
         found_non_existing[c] = 0;
     }
@@ -2153,7 +2189,8 @@ time_t rrd_stats_json(int type, RRDSET *st, BUFFER *wb, long points, long group,
         long count = 0, printed = 0, group_count = 0;
         last_timestamp = 0;
 
-        if(st->debug) debug(D_RRD_STATS, "%s: REQUEST after:%u before:%u, points:%ld, group:%ld, CHART cur:%ld first: %u last:%u, CALC start_t:%ld, stop_t:%ld"
+        if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+            debug(D_RRD_STATS, "%s: REQUEST after:%u before:%u, points:%ld, group:%ld, CHART cur:%ld first: %u last:%u, CALC start_t:%ld, stop_t:%ld"
                     , st->id
                     , (uint32_t)after
                     , (uint32_t)before
@@ -2173,7 +2210,8 @@ time_t rrd_stats_json(int type, RRDSET *st, BUFFER *wb, long points, long group,
 
             int print_this = 0;
 
-            if(st->debug) debug(D_RRD_STATS, "%s t = %ld, count = %ld, group_count = %ld, printed = %ld, now = %ld, %s %s"
+            if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+                debug(D_RRD_STATS, "%s t = %ld, count = %ld, group_count = %ld, printed = %ld, now = %ld, %s %s"
                     , st->id
                     , t
                     , count + 1
@@ -2311,6 +2349,6 @@ time_t rrd_stats_json(int type, RRDSET *st, BUFFER *wb, long points, long group,
 
     debug(D_RRD_STATS, "RRD_STATS_JSON: %s total %zu bytes", st->name, wb->len);
 
-    pthread_rwlock_unlock(&st->rwlock);
+    rrdset_unlock(st);
     return last_timestamp;
 }
index 7b14019708e32f54b3a18703e938320ac278c509..18d54b7b1a7580d18176ee6b50c3df1f7b1287e8 100644 (file)
 #define RRDR_OPTION_NOT_ALIGNED     0x00001000 // do not align charts for persistant timeframes
 
 extern void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb);
-extern void rrd_stats_api_v1_charts(BUFFER *wb);
+extern void rrd_stats_api_v1_charts(RRDHOST *host, BUFFER *wb);
 
-extern void rrd_stats_api_v1_charts_allmetrics_shell(BUFFER *wb);
-extern void rrd_stats_api_v1_charts_allmetrics_prometheus(BUFFER *wb);
+extern void rrd_stats_api_v1_charts_allmetrics_shell(RRDHOST *host, BUFFER *wb);
+extern void rrd_stats_api_v1_charts_allmetrics_prometheus(RRDHOST *host, BUFFER *wb);
 
 extern unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb);
 
 extern void rrd_stats_graph_json(RRDSET *st, char *options, BUFFER *wb);
 
-extern void rrd_stats_all_json(BUFFER *wb);
+extern void rrd_stats_all_json(RRDHOST *host, BUFFER *wb);
 
 extern time_t rrd_stats_json(int type, RRDSET *st, BUFFER *wb, long entries_to_show, long group, int group_method, time_t after, time_t before, int only_non_zero);
 
diff --git a/src/rrdcalc.c b/src/rrdcalc.c
new file mode 100644 (file)
index 0000000..1f18454
--- /dev/null
@@ -0,0 +1,415 @@
+#define NETDATA_HEALTH_INTERNALS
+#include "common.h"
+
+// ----------------------------------------------------------------------------
+// RRDCALC management
+
+inline const char *rrdcalc_status2string(int status) {
+    switch(status) {
+        case RRDCALC_STATUS_REMOVED:
+            return "REMOVED";
+
+        case RRDCALC_STATUS_UNDEFINED:
+            return "UNDEFINED";
+
+        case RRDCALC_STATUS_UNINITIALIZED:
+            return "UNINITIALIZED";
+
+        case RRDCALC_STATUS_CLEAR:
+            return "CLEAR";
+
+        case RRDCALC_STATUS_RAISED:
+            return "RAISED";
+
+        case RRDCALC_STATUS_WARNING:
+            return "WARNING";
+
+        case RRDCALC_STATUS_CRITICAL:
+            return "CRITICAL";
+
+        default:
+            error("Unknown alarm status %d", status);
+            return "UNKNOWN";
+    }
+}
+
+static void rrdsetcalc_link(RRDSET *st, RRDCALC *rc) {
+    debug(D_HEALTH, "Health linking alarm '%s.%s' to chart '%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, st->id, st->rrdhost->hostname);
+
+    rc->last_status_change = now_realtime_sec();
+    rc->rrdset = st;
+
+    rc->rrdset_next = st->alarms;
+    rc->rrdset_prev = NULL;
+
+    if(rc->rrdset_next)
+        rc->rrdset_next->rrdset_prev = rc;
+
+    st->alarms = rc;
+
+    if(rc->update_every < rc->rrdset->update_every) {
+        error("Health alarm '%s.%s' has update every %d, less than chart update every %d. Setting alarm update frequency to %d.", rc->rrdset->id, rc->name, rc->update_every, rc->rrdset->update_every, rc->rrdset->update_every);
+        rc->update_every = rc->rrdset->update_every;
+    }
+
+    if(!isnan(rc->green) && isnan(st->green)) {
+        debug(D_HEALTH, "Health alarm '%s.%s' green threshold set from %Lf to %Lf.", rc->rrdset->id, rc->name, rc->rrdset->green, rc->green);
+        st->green = rc->green;
+    }
+
+    if(!isnan(rc->red) && isnan(st->red)) {
+        debug(D_HEALTH, "Health alarm '%s.%s' red threshold set from %Lf to %Lf.", rc->rrdset->id, rc->name, rc->rrdset->red, rc->red);
+        st->red = rc->red;
+    }
+
+    rc->local  = rrdvar_create_and_index("local",  &st->variables_root_index, rc->name, RRDVAR_TYPE_CALCULATED, &rc->value);
+    rc->family = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rc->name, RRDVAR_TYPE_CALCULATED, &rc->value);
+
+    char fullname[RRDVAR_MAX_LENGTH + 1];
+    snprintfz(fullname, RRDVAR_MAX_LENGTH, "%s.%s", st->id, rc->name);
+    rc->hostid   = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, fullname, RRDVAR_TYPE_CALCULATED, &rc->value);
+
+    snprintfz(fullname, RRDVAR_MAX_LENGTH, "%s.%s", st->name, rc->name);
+    rc->hostname = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, fullname, RRDVAR_TYPE_CALCULATED, &rc->value);
+
+    if(!rc->units) rc->units = strdupz(st->units);
+
+    {
+        time_t now = now_realtime_sec();
+        health_alarm_log(
+                st->rrdhost,
+                rc->id,
+                rc->next_event_id++,
+                now,
+                rc->name,
+                rc->rrdset->id,
+                rc->rrdset->family,
+                rc->exec,
+                rc->recipient,
+                now - rc->last_status_change,
+                rc->old_value,
+                rc->value,
+                rc->status,
+                RRDCALC_STATUS_UNINITIALIZED,
+                rc->source,
+                rc->units,
+                rc->info,
+                0,
+                0
+        );
+    }
+}
+
+static inline int rrdcalc_is_matching_this_rrdset(RRDCALC *rc, RRDSET *st) {
+    if(     (rc->hash_chart == st->hash      && !strcmp(rc->chart, st->id)) ||
+            (rc->hash_chart == st->hash_name && !strcmp(rc->chart, st->name)))
+        return 1;
+
+    return 0;
+}
+
+// this has to be called while the RRDHOST is locked
+inline void rrdsetcalc_link_matching(RRDSET *st) {
+    // debug(D_HEALTH, "find matching alarms for chart '%s'", st->id);
+
+    RRDCALC *rc;
+    for(rc = st->rrdhost->alarms; rc ; rc = rc->next) {
+        if(unlikely(rc->rrdset))
+            continue;
+
+        if(unlikely(rrdcalc_is_matching_this_rrdset(rc, st)))
+            rrdsetcalc_link(st, rc);
+    }
+}
+
+// this has to be called while the RRDHOST is locked
+inline void rrdsetcalc_unlink(RRDCALC *rc) {
+    RRDSET *st = rc->rrdset;
+
+    if(!st) {
+        debug(D_HEALTH, "Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rc->chart?rc->chart:"NOCHART", rc->name);
+        error("Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rc->chart?rc->chart:"NOCHART", rc->name);
+        return;
+    }
+
+    {
+        time_t now = now_realtime_sec();
+        health_alarm_log(
+                st->rrdhost,
+                rc->id,
+                rc->next_event_id++,
+                now,
+                rc->name,
+                rc->rrdset->id,
+                rc->rrdset->family,
+                rc->exec,
+                rc->recipient,
+                now - rc->last_status_change,
+                rc->old_value,
+                rc->value,
+                rc->status,
+                RRDCALC_STATUS_REMOVED,
+                rc->source,
+                rc->units,
+                rc->info,
+                0,
+                0
+        );
+    }
+
+    RRDHOST *host = st->rrdhost;
+
+    debug(D_HEALTH, "Health unlinking alarm '%s.%s' from chart '%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, st->id, host->hostname);
+
+    // unlink it
+    if(rc->rrdset_prev)
+        rc->rrdset_prev->rrdset_next = rc->rrdset_next;
+
+    if(rc->rrdset_next)
+        rc->rrdset_next->rrdset_prev = rc->rrdset_prev;
+
+    if(st->alarms == rc)
+        st->alarms = rc->rrdset_next;
+
+    rc->rrdset_prev = rc->rrdset_next = NULL;
+
+    rrdvar_free(st->rrdhost, &st->variables_root_index, rc->local);
+    rc->local = NULL;
+
+    rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rc->family);
+    rc->family = NULL;
+
+    rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rc->hostid);
+    rc->hostid = NULL;
+
+    rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rc->hostname);
+    rc->hostname = NULL;
+
+    rc->rrdset = NULL;
+
+    // RRDCALC will remain in RRDHOST
+    // so that if the matching chart is found in the future
+    // it will be applied automatically
+}
+
+RRDCALC *rrdcalc_find(RRDSET *st, const char *name) {
+    RRDCALC *rc;
+    uint32_t hash = simple_hash(name);
+
+    for( rc = st->alarms; rc ; rc = rc->rrdset_next ) {
+        if(unlikely(rc->hash == hash && !strcmp(rc->name, name)))
+            return rc;
+    }
+
+    return NULL;
+}
+
+inline int rrdcalc_exists(RRDHOST *host, const char *chart, const char *name, uint32_t hash_chart, uint32_t hash_name) {
+    RRDCALC *rc;
+
+    if(unlikely(!chart)) {
+        error("attempt to find RRDCALC '%s' without giving a chart name", name);
+        return 1;
+    }
+
+    if(unlikely(!hash_chart)) hash_chart = simple_hash(chart);
+    if(unlikely(!hash_name))  hash_name  = simple_hash(name);
+
+    // make sure it does not already exist
+    for(rc = host->alarms; rc ; rc = rc->next) {
+        if (unlikely(rc->chart && rc->hash == hash_name && rc->hash_chart == hash_chart && !strcmp(name, rc->name) && !strcmp(chart, rc->chart))) {
+            debug(D_HEALTH, "Health alarm '%s.%s' already exists in host '%s'.", chart, name, host->hostname);
+            error("Health alarm '%s.%s' already exists in host '%s'.", chart, name, host->hostname);
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+inline uint32_t rrdcalc_get_unique_id(RRDHOST *host, const char *chart, const char *name, uint32_t *next_event_id) {
+    if(chart && name) {
+        uint32_t hash_chart = simple_hash(chart);
+        uint32_t hash_name = simple_hash(name);
+
+        // re-use old IDs, by looking them up in the alarm log
+        ALARM_ENTRY *ae;
+        for(ae = host->health_log.alarms; ae ;ae = ae->next) {
+            if(unlikely(ae->hash_name == hash_name && ae->hash_chart == hash_chart && !strcmp(name, ae->name) && !strcmp(chart, ae->chart))) {
+                if(next_event_id) *next_event_id = ae->alarm_event_id + 1;
+                return ae->alarm_id;
+            }
+        }
+    }
+
+    return host->health_log.next_alarm_id++;
+}
+
+inline void rrdcalc_create_part2(RRDHOST *host, RRDCALC *rc) {
+    rrdhost_check_rdlock(host);
+
+    if(rc->calculation) {
+        rc->calculation->status = &rc->status;
+        rc->calculation->this = &rc->value;
+        rc->calculation->after = &rc->db_after;
+        rc->calculation->before = &rc->db_before;
+        rc->calculation->rrdcalc = rc;
+    }
+
+    if(rc->warning) {
+        rc->warning->status = &rc->status;
+        rc->warning->this = &rc->value;
+        rc->warning->after = &rc->db_after;
+        rc->warning->before = &rc->db_before;
+        rc->warning->rrdcalc = rc;
+    }
+
+    if(rc->critical) {
+        rc->critical->status = &rc->status;
+        rc->critical->this = &rc->value;
+        rc->critical->after = &rc->db_after;
+        rc->critical->before = &rc->db_before;
+        rc->critical->rrdcalc = rc;
+    }
+
+    // link it to the host
+    if(likely(host->alarms)) {
+        // append it
+        RRDCALC *t;
+        for(t = host->alarms; t && t->next ; t = t->next) ;
+        t->next = rc;
+    }
+    else {
+        host->alarms = rc;
+    }
+
+    // link it to its chart
+    RRDSET *st;
+    rrdset_foreach_read(st, host) {
+        if(rrdcalc_is_matching_this_rrdset(rc, st)) {
+            rrdsetcalc_link(st, rc);
+            break;
+        }
+    }
+}
+
+inline RRDCALC *rrdcalc_create(RRDHOST *host, RRDCALCTEMPLATE *rt, const char *chart) {
+
+    debug(D_HEALTH, "Health creating dynamic alarm (from template) '%s.%s'", chart, rt->name);
+
+    if(rrdcalc_exists(host, chart, rt->name, 0, 0))
+        return NULL;
+
+    RRDCALC *rc = callocz(1, sizeof(RRDCALC));
+    rc->next_event_id = 1;
+    rc->id = rrdcalc_get_unique_id(host, chart, rt->name, &rc->next_event_id);
+    rc->name = strdupz(rt->name);
+    rc->hash = simple_hash(rc->name);
+    rc->chart = strdupz(chart);
+    rc->hash_chart = simple_hash(rc->chart);
+
+    if(rt->dimensions) rc->dimensions = strdupz(rt->dimensions);
+
+    rc->green = rt->green;
+    rc->red = rt->red;
+    rc->value = NAN;
+    rc->old_value = NAN;
+
+    rc->delay_up_duration = rt->delay_up_duration;
+    rc->delay_down_duration = rt->delay_down_duration;
+    rc->delay_max_duration = rt->delay_max_duration;
+    rc->delay_multiplier = rt->delay_multiplier;
+
+    rc->group = rt->group;
+    rc->after = rt->after;
+    rc->before = rt->before;
+    rc->update_every = rt->update_every;
+    rc->options = rt->options;
+
+    if(rt->exec) rc->exec = strdupz(rt->exec);
+    if(rt->recipient) rc->recipient = strdupz(rt->recipient);
+    if(rt->source) rc->source = strdupz(rt->source);
+    if(rt->units) rc->units = strdupz(rt->units);
+    if(rt->info) rc->info = strdupz(rt->info);
+
+    if(rt->calculation) {
+        rc->calculation = expression_parse(rt->calculation->source, NULL, NULL);
+        if(!rc->calculation)
+            error("Health alarm '%s.%s': failed to parse calculation expression '%s'", chart, rt->name, rt->calculation->source);
+    }
+    if(rt->warning) {
+        rc->warning = expression_parse(rt->warning->source, NULL, NULL);
+        if(!rc->warning)
+            error("Health alarm '%s.%s': failed to re-parse warning expression '%s'", chart, rt->name, rt->warning->source);
+    }
+    if(rt->critical) {
+        rc->critical = expression_parse(rt->critical->source, NULL, NULL);
+        if(!rc->critical)
+            error("Health alarm '%s.%s': failed to re-parse critical expression '%s'", chart, rt->name, rt->critical->source);
+    }
+
+    debug(D_HEALTH, "Health runtime added alarm '%s.%s': exec '%s', recipient '%s', green %Lf, red %Lf, lookup: group %d, after %d, before %d, options %u, dimensions '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s', delay up %d, delay down %d, delay max %d, delay_multiplier %f",
+            (rc->chart)?rc->chart:"NOCHART",
+            rc->name,
+            (rc->exec)?rc->exec:"DEFAULT",
+            (rc->recipient)?rc->recipient:"DEFAULT",
+            rc->green,
+            rc->red,
+            rc->group,
+            rc->after,
+            rc->before,
+            rc->options,
+            (rc->dimensions)?rc->dimensions:"NONE",
+            rc->update_every,
+            (rc->calculation)?rc->calculation->parsed_as:"NONE",
+            (rc->warning)?rc->warning->parsed_as:"NONE",
+            (rc->critical)?rc->critical->parsed_as:"NONE",
+            rc->source,
+            rc->delay_up_duration,
+            rc->delay_down_duration,
+            rc->delay_max_duration,
+            rc->delay_multiplier
+    );
+
+    rrdcalc_create_part2(host, rc);
+    return rc;
+}
+
+void rrdcalc_free(RRDHOST *host, RRDCALC *rc) {
+    if(!rc) return;
+
+    debug(D_HEALTH, "Health removing alarm '%s.%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, host->hostname);
+
+    // unlink it from RRDSET
+    if(rc->rrdset) rrdsetcalc_unlink(rc);
+
+    // unlink it from RRDHOST
+    if(unlikely(rc == host->alarms))
+        host->alarms = rc->next;
+
+    else {
+        RRDCALC *t;
+        for(t = host->alarms; t && t->next != rc; t = t->next) ;
+        if(t) {
+            t->next = rc->next;
+            rc->next = NULL;
+        }
+        else
+            error("Cannot unlink alarm '%s.%s' from host '%s': not found", rc->chart?rc->chart:"NOCHART", rc->name, host->hostname);
+    }
+
+    expression_free(rc->calculation);
+    expression_free(rc->warning);
+    expression_free(rc->critical);
+
+    freez(rc->name);
+    freez(rc->chart);
+    freez(rc->family);
+    freez(rc->dimensions);
+    freez(rc->exec);
+    freez(rc->recipient);
+    freez(rc->source);
+    freez(rc->units);
+    freez(rc->info);
+    freez(rc);
+}
diff --git a/src/rrdcalctemplate.c b/src/rrdcalctemplate.c
new file mode 100644 (file)
index 0000000..1deec43
--- /dev/null
@@ -0,0 +1,60 @@
+#define NETDATA_HEALTH_INTERNALS
+#include "common.h"
+
+// ----------------------------------------------------------------------------
+// RRDCALCTEMPLATE management
+
+void rrdcalctemplate_link_matching(RRDSET *st) {
+    RRDCALCTEMPLATE *rt;
+
+    for(rt = st->rrdhost->templates; rt ; rt = rt->next) {
+        if(rt->hash_context == st->hash_context && !strcmp(rt->context, st->context)
+           && (!rt->family_pattern || simple_pattern_matches(rt->family_pattern, st->family))) {
+            RRDCALC *rc = rrdcalc_create(st->rrdhost, rt, st->id);
+            if(unlikely(!rc))
+                error("Health tried to create alarm from template '%s', but it failed", rt->name);
+
+#ifdef NETDATA_INTERNAL_CHECKS
+            else if(rc->rrdset != st)
+                error("Health alarm '%s.%s' should be linked to chart '%s', but it is not", rc->chart?rc->chart:"NOCHART", rc->name, st->id);
+#endif
+        }
+    }
+}
+
+inline void rrdcalctemplate_free(RRDHOST *host, RRDCALCTEMPLATE *rt) {
+    debug(D_HEALTH, "Health removing template '%s' of host '%s'", rt->name, host->hostname);
+
+    if(host->templates == rt) {
+        host->templates = rt->next;
+    }
+    else {
+        RRDCALCTEMPLATE *t;
+        for (t = host->templates; t && t->next != rt; t = t->next ) ;
+        if(t) {
+            t->next = rt->next;
+            rt->next = NULL;
+        }
+        else
+            error("Cannot find RRDCALCTEMPLATE '%s' linked in host '%s'", rt->name, host->hostname);
+    }
+
+    expression_free(rt->calculation);
+    expression_free(rt->warning);
+    expression_free(rt->critical);
+
+    freez(rt->family_match);
+    simple_pattern_free(rt->family_pattern);
+
+    freez(rt->name);
+    freez(rt->exec);
+    freez(rt->recipient);
+    freez(rt->context);
+    freez(rt->source);
+    freez(rt->units);
+    freez(rt->info);
+    freez(rt->dimensions);
+    freez(rt);
+}
+
+
diff --git a/src/rrddim.c b/src/rrddim.c
new file mode 100644 (file)
index 0000000..54a1752
--- /dev/null
@@ -0,0 +1,313 @@
+#define NETDATA_RRD_INTERNALS 1
+#include "common.h"
+
+// ----------------------------------------------------------------------------
+// RRDDIM index
+
+int rrddim_compare(void* a, void* b) {
+    if(((RRDDIM *)a)->hash < ((RRDDIM *)b)->hash) return -1;
+    else if(((RRDDIM *)a)->hash > ((RRDDIM *)b)->hash) return 1;
+    else return strcmp(((RRDDIM *)a)->id, ((RRDDIM *)b)->id);
+}
+
+#define rrddim_index_add(st, rd) (RRDDIM *)avl_insert_lock(&((st)->dimensions_index), (avl *)(rd))
+#define rrddim_index_del(st,rd ) (RRDDIM *)avl_remove_lock(&((st)->dimensions_index), (avl *)(rd))
+
+static inline RRDDIM *rrddim_index_find(RRDSET *st, const char *id, uint32_t hash) {
+    RRDDIM tmp = {
+            .id = id,
+            .hash = (hash)?hash:simple_hash(id)
+    };
+    return (RRDDIM *)avl_search_lock(&(st->dimensions_index), (avl *) &tmp);
+}
+
+
+// ----------------------------------------------------------------------------
+// RRDDIM - find a dimension
+
+inline RRDDIM *rrddim_find(RRDSET *st, const char *id) {
+    debug(D_RRD_CALLS, "rrddim_find() for chart %s, dimension %s", st->name, id);
+
+    return rrddim_index_find(st, id, 0);
+}
+
+
+// ----------------------------------------------------------------------------
+// RRDDIM rename a dimension
+
+inline void rrddim_set_name(RRDSET *st, RRDDIM *rd, const char *name) {
+    if(unlikely(!strcmp(rd->name, name)))
+        return;
+
+    debug(D_RRD_CALLS, "rrddim_set_name() from %s.%s to %s.%s", st->name, rd->name, st->name, name);
+
+    char varname[CONFIG_MAX_NAME + 1];
+    snprintfz(varname, CONFIG_MAX_NAME, "dim %s name", rd->id);
+    rd->name = config_set_default(st->config_section, varname, name);
+    rd->hash_name = simple_hash(rd->name);
+
+    rrddimvar_rename_all(rd);
+}
+
+
+// ----------------------------------------------------------------------------
+// RRDDIM create a dimension
+
+RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, collected_number multiplier, collected_number divisor, RRD_ALGORITHM algorithm) {
+    RRDDIM *rd = rrddim_find(st, id);
+    if(unlikely(rd)) {
+        debug(D_RRD_CALLS, "Cannot create rrd dimension '%s/%s', it already exists.", st->id, name?name:"<NONAME>");
+        return rd;
+    }
+
+    char filename[FILENAME_MAX + 1];
+    char fullfilename[FILENAME_MAX + 1];
+
+    char varname[CONFIG_MAX_NAME + 1];
+    unsigned long size = sizeof(RRDDIM) + (st->entries * sizeof(storage_number));
+
+    debug(D_RRD_CALLS, "Adding dimension '%s/%s'.", st->id, id);
+
+    rrdset_strncpyz_name(filename, id, FILENAME_MAX);
+    snprintfz(fullfilename, FILENAME_MAX, "%s/%s.db", st->cache_dir, filename);
+
+    if(st->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || st->rrd_memory_mode == RRD_MEMORY_MODE_MAP) {
+        rd = (RRDDIM *)mymmap(fullfilename, size, ((st->rrd_memory_mode == RRD_MEMORY_MODE_MAP) ? MAP_SHARED : MAP_PRIVATE), 1);
+        if(likely(rd)) {
+            // we have a file mapped for rd
+
+            memset(&rd->avl, 0, sizeof(avl));
+            rd->id = NULL;
+            rd->name = NULL;
+            rd->cache_filename = NULL;
+            rd->variables = NULL;
+            rd->next = NULL;
+            rd->rrdset = NULL;
+
+            struct timeval now;
+            now_realtime_timeval(&now);
+
+            if(strcmp(rd->magic, RRDDIMENSION_MAGIC) != 0) {
+                errno = 0;
+                info("Initializing file %s.", fullfilename);
+                memset(rd, 0, size);
+            }
+            else if(rd->memsize != size) {
+                errno = 0;
+                error("File %s does not have the desired size. Clearing it.", fullfilename);
+                memset(rd, 0, size);
+            }
+            else if(rd->multiplier != multiplier) {
+                errno = 0;
+                error("File %s does not have the same multiplier. Clearing it.", fullfilename);
+                memset(rd, 0, size);
+            }
+            else if(rd->divisor != divisor) {
+                errno = 0;
+                error("File %s does not have the same divisor. Clearing it.", fullfilename);
+                memset(rd, 0, size);
+            }
+            else if(rd->update_every != st->update_every) {
+                errno = 0;
+                error("File %s does not have the same refresh frequency. Clearing it.", fullfilename);
+                memset(rd, 0, size);
+            }
+            else if(dt_usec(&now, &rd->last_collected_time) > (rd->entries * rd->update_every * USEC_PER_SEC)) {
+                errno = 0;
+                error("File %s is too old. Clearing it.", fullfilename);
+                memset(rd, 0, size);
+            }
+
+            if(rd->algorithm && rd->algorithm != algorithm)
+                error("File %s does not have the expected algorithm (expected %u '%s', found %u '%s'). Previous values may be wrong."
+                      , fullfilename, algorithm, rrd_algorithm_name(algorithm), rd->algorithm,
+                        rrd_algorithm_name(rd->algorithm));
+
+            // make sure we have the right memory mode
+            // even if we cleared the memory
+            rd->rrd_memory_mode = st->rrd_memory_mode;
+        }
+    }
+
+    if(unlikely(!rd)) {
+        // if we didn't manage to get a mmap'd dimension, just create one
+        rd = callocz(1, size);
+        rd->rrd_memory_mode = (st->rrd_memory_mode == RRD_MEMORY_MODE_NONE) ? RRD_MEMORY_MODE_NONE : RRD_MEMORY_MODE_RAM;
+    }
+
+    rd->memsize = size;
+
+    strcpy(rd->magic, RRDDIMENSION_MAGIC);
+
+    rd->id = strdupz(id);
+    rd->hash = simple_hash(rd->id);
+
+    rd->cache_filename = strdupz(fullfilename);
+
+    snprintfz(varname, CONFIG_MAX_NAME, "dim %s name", rd->id);
+    rd->name = config_get(st->config_section, varname, (name && *name)?name:rd->id);
+    rd->hash_name = simple_hash(rd->name);
+
+    snprintfz(varname, CONFIG_MAX_NAME, "dim %s algorithm", rd->id);
+    rd->algorithm = rrd_algorithm_id(config_get(st->config_section, varname, rrd_algorithm_name(algorithm)));
+
+    snprintfz(varname, CONFIG_MAX_NAME, "dim %s multiplier", rd->id);
+    rd->multiplier = config_get_number(st->config_section, varname, multiplier);
+
+    snprintfz(varname, CONFIG_MAX_NAME, "dim %s divisor", rd->id);
+    rd->divisor = config_get_number(st->config_section, varname, divisor);
+    if(!rd->divisor) rd->divisor = 1;
+
+    rd->entries = st->entries;
+    rd->update_every = st->update_every;
+
+    // prevent incremental calculation spikes
+    rd->collections_counter = 0;
+    rd->updated = 0;
+    rd->flags = 0x00000000;
+
+    rd->calculated_value = 0;
+    rd->last_calculated_value = 0;
+    rd->collected_value = 0;
+    rd->last_collected_value = 0;
+    rd->collected_volume = 0;
+    rd->stored_volume = 0;
+    rd->last_stored_value = 0;
+    rd->values[st->current_entry] = pack_storage_number(0, SN_NOT_EXISTS);
+    rd->last_collected_time.tv_sec = 0;
+    rd->last_collected_time.tv_usec = 0;
+    rd->rrdset = st;
+
+    // append this dimension
+    rrdset_wrlock(st);
+    if(!st->dimensions)
+        st->dimensions = rd;
+    else {
+        RRDDIM *td = st->dimensions;
+        for(; td->next; td = td->next) ;
+        td->next = rd;
+    }
+
+    if(st->rrdhost->health_enabled) {
+        rrddimvar_create(rd, RRDVAR_TYPE_CALCULATED, NULL, NULL, &rd->last_stored_value, 0);
+        rrddimvar_create(rd, RRDVAR_TYPE_COLLECTED, NULL, "_raw", &rd->last_collected_value, 0);
+        rrddimvar_create(rd, RRDVAR_TYPE_TIME_T, NULL, "_last_collected_t", &rd->last_collected_time.tv_sec, 0);
+    }
+
+    rrdset_unlock(st);
+
+    if(unlikely(rrddim_index_add(st, rd) != rd))
+        error("RRDDIM: INTERNAL ERROR: attempt to index duplicate dimension '%s' on chart '%s'", rd->id, st->id);
+
+    return(rd);
+}
+
+
+// ----------------------------------------------------------------------------
+// RRDDIM remove / free a dimension
+
+void rrddim_free(RRDSET *st, RRDDIM *rd)
+{
+    debug(D_RRD_CALLS, "rrddim_free() %s.%s", st->name, rd->name);
+
+    if(rd == st->dimensions)
+        st->dimensions = rd->next;
+    else {
+        RRDDIM *i;
+        for (i = st->dimensions; i && i->next != rd; i = i->next) ;
+
+        if (i && i->next == rd)
+            i->next = rd->next;
+        else
+            error("Request to free dimension '%s.%s' but it is not linked.", st->id, rd->name);
+    }
+    rd->next = NULL;
+
+    while(rd->variables)
+        rrddimvar_free(rd->variables);
+
+    if(unlikely(rrddim_index_del(st, rd) != rd))
+        error("RRDDIM: INTERNAL ERROR: attempt to remove from index dimension '%s' on chart '%s', removed a different dimension.", rd->id, st->id);
+
+    // free(rd->annotations);
+
+    switch(rd->rrd_memory_mode) {
+        case RRD_MEMORY_MODE_SAVE:
+            debug(D_RRD_CALLS, "Saving dimension '%s' to '%s'.", rd->name, rd->cache_filename);
+            savememory(rd->cache_filename, rd, rd->memsize);
+            // continue to map mode - no break;
+
+        case RRD_MEMORY_MODE_MAP:
+            debug(D_RRD_CALLS, "Unmapping dimension '%s'.", rd->name);
+            freez((void *)rd->id);
+            freez(rd->cache_filename);
+            munmap(rd, rd->memsize);
+            break;
+
+        case RRD_MEMORY_MODE_NONE:
+        case RRD_MEMORY_MODE_RAM:
+            debug(D_RRD_CALLS, "Removing dimension '%s'.", rd->name);
+            freez((void *)rd->id);
+            freez(rd->cache_filename);
+            freez(rd);
+            break;
+    }
+}
+
+
+// ----------------------------------------------------------------------------
+// RRDDIM - set dimension options
+
+int rrddim_hide(RRDSET *st, const char *id) {
+    debug(D_RRD_CALLS, "rrddim_hide() for chart %s, dimension %s", st->name, id);
+
+    RRDDIM *rd = rrddim_find(st, id);
+    if(unlikely(!rd)) {
+        error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
+        return 1;
+    }
+
+    rrddim_flag_set(rd, RRDDIM_FLAG_HIDDEN);
+    return 0;
+}
+
+int rrddim_unhide(RRDSET *st, const char *id) {
+    debug(D_RRD_CALLS, "rrddim_unhide() for chart %s, dimension %s", st->name, id);
+
+    RRDDIM *rd = rrddim_find(st, id);
+    if(unlikely(!rd)) {
+        error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
+        return 1;
+    }
+
+    rrddim_flag_clear(rd, RRDDIM_FLAG_HIDDEN);
+    return 0;
+}
+
+
+// ----------------------------------------------------------------------------
+// RRDDIM - collect values for a dimension
+
+inline collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value) {
+    debug(D_RRD_CALLS, "rrddim_set_by_pointer() for chart %s, dimension %s, value " COLLECTED_NUMBER_FORMAT, st->name, rd->name, value);
+
+    now_realtime_timeval(&rd->last_collected_time);
+    rd->collected_value = value;
+    rd->updated = 1;
+
+    rd->collections_counter++;
+
+    // fprintf(stderr, "%s.%s %llu " COLLECTED_NUMBER_FORMAT " dt %0.6f" " rate " CALCULATED_NUMBER_FORMAT "\n", st->name, rd->name, st->usec_since_last_update, value, (float)((double)st->usec_since_last_update / (double)1000000), (calculated_number)((value - rd->last_collected_value) * (calculated_number)rd->multiplier / (calculated_number)rd->divisor * 1000000.0 / (calculated_number)st->usec_since_last_update));
+
+    return rd->last_collected_value;
+}
+
+collected_number rrddim_set(RRDSET *st, const char *id, collected_number value) {
+    RRDDIM *rd = rrddim_find(st, id);
+    if(unlikely(!rd)) {
+        error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
+        return 0;
+    }
+
+    return rrddim_set_by_pointer(st, rd, value);
+}
diff --git a/src/rrddimvar.c b/src/rrddimvar.c
new file mode 100644 (file)
index 0000000..f6eb6d8
--- /dev/null
@@ -0,0 +1,210 @@
+#define NETDATA_HEALTH_INTERNALS
+#include "common.h"
+
+// ----------------------------------------------------------------------------
+// RRDDIMVAR management
+// DIMENSION VARIABLES
+
+#define RRDDIMVAR_ID_MAX 1024
+
+static inline void rrddimvar_free_variables(RRDDIMVAR *rs) {
+    RRDDIM *rd = rs->rrddim;
+    RRDSET *st = rd->rrdset;
+
+    // CHART VARIABLES FOR THIS DIMENSION
+
+    rrdvar_free(st->rrdhost, &st->variables_root_index, rs->var_local_id);
+    rs->var_local_id = NULL;
+
+    rrdvar_free(st->rrdhost, &st->variables_root_index, rs->var_local_name);
+    rs->var_local_name = NULL;
+
+    // FAMILY VARIABLES FOR THIS DIMENSION
+
+    rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_id);
+    rs->var_family_id = NULL;
+
+    rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_name);
+    rs->var_family_name = NULL;
+
+    rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_contextid);
+    rs->var_family_contextid = NULL;
+
+    rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_contextname);
+    rs->var_family_contextname = NULL;
+
+    // HOST VARIABLES FOR THIS DIMENSION
+
+    rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartidid);
+    rs->var_host_chartidid = NULL;
+
+    rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartidname);
+    rs->var_host_chartidname = NULL;
+
+    rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartnameid);
+    rs->var_host_chartnameid = NULL;
+
+    rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartnamename);
+    rs->var_host_chartnamename = NULL;
+
+    // KEYS
+
+    freez(rs->key_id);
+    rs->key_id = NULL;
+
+    freez(rs->key_name);
+    rs->key_name = NULL;
+
+    freez(rs->key_fullidid);
+    rs->key_fullidid = NULL;
+
+    freez(rs->key_fullidname);
+    rs->key_fullidname = NULL;
+
+    freez(rs->key_contextid);
+    rs->key_contextid = NULL;
+
+    freez(rs->key_contextname);
+    rs->key_contextname = NULL;
+
+    freez(rs->key_fullnameid);
+    rs->key_fullnameid = NULL;
+
+    freez(rs->key_fullnamename);
+    rs->key_fullnamename = NULL;
+}
+
+static inline void rrddimvar_create_variables(RRDDIMVAR *rs) {
+    rrddimvar_free_variables(rs);
+
+    RRDDIM *rd = rs->rrddim;
+    RRDSET *st = rd->rrdset;
+
+    char buffer[RRDDIMVAR_ID_MAX + 1];
+
+    // KEYS
+
+    snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->id, rs->suffix);
+    rs->key_id = strdupz(buffer);
+
+    snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->name, rs->suffix);
+    rs->key_name = strdupz(buffer);
+
+    snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->key_id);
+    rs->key_fullidid = strdupz(buffer);
+
+    snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->key_name);
+    rs->key_fullidname = strdupz(buffer);
+
+    snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->context, rs->key_id);
+    rs->key_contextid = strdupz(buffer);
+
+    snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->context, rs->key_name);
+    rs->key_contextname = strdupz(buffer);
+
+    snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->key_id);
+    rs->key_fullnameid = strdupz(buffer);
+
+    snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->key_name);
+    rs->key_fullnamename = strdupz(buffer);
+
+    // CHART VARIABLES FOR THIS DIMENSION
+    // -----------------------------------
+    //
+    // dimensions are available as:
+    // - $id
+    // - $name
+
+    rs->var_local_id           = rrdvar_create_and_index("local", &st->variables_root_index, rs->key_id, rs->type, rs->value);
+    rs->var_local_name         = rrdvar_create_and_index("local", &st->variables_root_index, rs->key_name, rs->type, rs->value);
+
+    // FAMILY VARIABLES FOR THIS DIMENSION
+    // -----------------------------------
+    //
+    // dimensions are available as:
+    // - $id                 (only the first, when multiple overlap)
+    // - $name               (only the first, when multiple overlap)
+    // - $chart-context.id
+    // - $chart-context.name
+
+    rs->var_family_id          = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_id, rs->type, rs->value);
+    rs->var_family_name        = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_name, rs->type, rs->value);
+    rs->var_family_contextid   = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_contextid, rs->type, rs->value);
+    rs->var_family_contextname = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_contextname, rs->type, rs->value);
+
+    // HOST VARIABLES FOR THIS DIMENSION
+    // -----------------------------------
+    //
+    // dimensions are available as:
+    // - $chart-id.id
+    // - $chart-id.name
+    // - $chart-name.id
+    // - $chart-name.name
+
+    rs->var_host_chartidid      = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullidid, rs->type, rs->value);
+    rs->var_host_chartidname    = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullidname, rs->type, rs->value);
+    rs->var_host_chartnameid    = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullnameid, rs->type, rs->value);
+    rs->var_host_chartnamename  = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullnamename, rs->type, rs->value);
+}
+
+RRDDIMVAR *rrddimvar_create(RRDDIM *rd, int type, const char *prefix, const char *suffix, void *value, uint32_t options) {
+    RRDSET *st = rd->rrdset;
+
+    debug(D_VARIABLES, "RRDDIMSET create for chart id '%s' name '%s', dimension id '%s', name '%s%s%s'", st->id, st->name, rd->id, (prefix)?prefix:"", rd->name, (suffix)?suffix:"");
+
+    if(!prefix) prefix = "";
+    if(!suffix) suffix = "";
+
+    RRDDIMVAR *rs = (RRDDIMVAR *)callocz(1, sizeof(RRDDIMVAR));
+
+    rs->prefix = strdupz(prefix);
+    rs->suffix = strdupz(suffix);
+
+    rs->type = type;
+    rs->value = value;
+    rs->options = options;
+    rs->rrddim = rd;
+
+    rs->next = rd->variables;
+    rd->variables = rs;
+
+    rrddimvar_create_variables(rs);
+
+    return rs;
+}
+
+void rrddimvar_rename_all(RRDDIM *rd) {
+    RRDSET *st = rd->rrdset;
+    debug(D_VARIABLES, "RRDDIMSET rename for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name);
+
+    RRDDIMVAR *rs, *next = rd->variables;
+    while((rs = next)) {
+        next = rs->next;
+        rrddimvar_create_variables(rs);
+    }
+}
+
+void rrddimvar_free(RRDDIMVAR *rs) {
+    RRDDIM *rd = rs->rrddim;
+    RRDSET *st = rd->rrdset;
+    debug(D_VARIABLES, "RRDDIMSET free for chart id '%s' name '%s', dimension id '%s', name '%s', prefix='%s', suffix='%s'", st->id, st->name, rd->id, rd->name, rs->prefix, rs->suffix);
+
+    rrddimvar_free_variables(rs);
+
+    if(rd->variables == rs) {
+        debug(D_VARIABLES, "RRDDIMSET removing first entry for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name);
+        rd->variables = rs->next;
+    }
+    else {
+        debug(D_VARIABLES, "RRDDIMSET removing non-first entry for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name);
+        RRDDIMVAR *t;
+        for (t = rd->variables; t && t->next != rs; t = t->next) ;
+        if(!t) error("RRDDIMVAR '%s' not found in dimension '%s/%s' variables linked list", rs->key_name, st->id, rd->id);
+        else t->next = rs->next;
+    }
+
+    freez(rs->prefix);
+    freez(rs->suffix);
+    freez(rs);
+}
+
diff --git a/src/rrdfamily.c b/src/rrdfamily.c
new file mode 100644 (file)
index 0000000..fc203e9
--- /dev/null
@@ -0,0 +1,58 @@
+#define NETDATA_RRD_INTERNALS 1
+#include "common.h"
+
+// ----------------------------------------------------------------------------
+// RRDFAMILY index
+
+int rrdfamily_compare(void *a, void *b) {
+    if(((RRDFAMILY *)a)->hash_family < ((RRDFAMILY *)b)->hash_family) return -1;
+    else if(((RRDFAMILY *)a)->hash_family > ((RRDFAMILY *)b)->hash_family) return 1;
+    else return strcmp(((RRDFAMILY *)a)->family, ((RRDFAMILY *)b)->family);
+}
+
+#define rrdfamily_index_add(host, rc) (RRDFAMILY *)avl_insert_lock(&((host)->rrdfamily_root_index), (avl *)(rc))
+#define rrdfamily_index_del(host, rc) (RRDFAMILY *)avl_remove_lock(&((host)->rrdfamily_root_index), (avl *)(rc))
+
+static RRDFAMILY *rrdfamily_index_find(RRDHOST *host, const char *id, uint32_t hash) {
+    RRDFAMILY tmp;
+    tmp.family = id;
+    tmp.hash_family = (hash)?hash:simple_hash(tmp.family);
+
+    return (RRDFAMILY *)avl_search_lock(&(host->rrdfamily_root_index), (avl *) &tmp);
+}
+
+RRDFAMILY *rrdfamily_create(RRDHOST *host, const char *id) {
+    RRDFAMILY *rc = rrdfamily_index_find(host, id, 0);
+    if(!rc) {
+        rc = callocz(1, sizeof(RRDFAMILY));
+
+        rc->family = strdupz(id);
+        rc->hash_family = simple_hash(rc->family);
+
+        // initialize the variables index
+        avl_init_lock(&rc->variables_root_index, rrdvar_compare);
+
+        RRDFAMILY *ret = rrdfamily_index_add(host, rc);
+        if(ret != rc)
+            fatal("RRDFAMILY: INTERNAL ERROR: Expected to INSERT RRDFAMILY '%s' into index, but inserted '%s'.", rc->family, (ret)?ret->family:"NONE");
+    }
+
+    rc->use_count++;
+    return rc;
+}
+
+void rrdfamily_free(RRDHOST *host, RRDFAMILY *rc) {
+    rc->use_count--;
+    if(!rc->use_count) {
+        RRDFAMILY *ret = rrdfamily_index_del(host, rc);
+        if(ret != rc)
+            fatal("RRDFAMILY: INTERNAL ERROR: Expected to DELETE RRDFAMILY '%s' from index, but deleted '%s'.", rc->family, (ret)?ret->family:"NONE");
+
+        if(rc->variables_root_index.avl_tree.root != NULL)
+            fatal("RRDFAMILY: INTERNAL ERROR: Variables index of RRDFAMILY '%s' that is freed, is not empty.", rc->family);
+
+        freez((void *)rc->family);
+        freez(rc);
+    }
+}
+
diff --git a/src/rrdhost.c b/src/rrdhost.c
new file mode 100644 (file)
index 0000000..de342bd
--- /dev/null
@@ -0,0 +1,512 @@
+#define NETDATA_RRD_INTERNALS 1
+#include "common.h"
+
+RRDHOST *localhost = NULL;
+size_t rrd_hosts_available = 0;
+pthread_rwlock_t rrd_rwlock = PTHREAD_RWLOCK_INITIALIZER;
+
+time_t rrdhost_free_orphan_time = 3600;
+
+// ----------------------------------------------------------------------------
+// RRDHOST index
+
+int rrdhost_compare(void* a, void* b) {
+    if(((RRDHOST *)a)->hash_machine_guid < ((RRDHOST *)b)->hash_machine_guid) return -1;
+    else if(((RRDHOST *)a)->hash_machine_guid > ((RRDHOST *)b)->hash_machine_guid) return 1;
+    else return strcmp(((RRDHOST *)a)->machine_guid, ((RRDHOST *)b)->machine_guid);
+}
+
+avl_tree_lock rrdhost_root_index = {
+        .avl_tree = { NULL, rrdhost_compare },
+        .rwlock = AVL_LOCK_INITIALIZER
+};
+
+RRDHOST *rrdhost_find_by_guid(const char *guid, uint32_t hash) {
+    debug(D_RRDHOST, "Searching in index for host with guid '%s'", guid);
+
+    RRDHOST tmp;
+    strncpyz(tmp.machine_guid, guid, GUID_LEN);
+    tmp.hash_machine_guid = (hash)?hash:simple_hash(tmp.machine_guid);
+
+    return (RRDHOST *)avl_search_lock(&(rrdhost_root_index), (avl *) &tmp);
+}
+
+RRDHOST *rrdhost_find_by_hostname(const char *hostname, uint32_t hash) {
+    if(unlikely(!strcmp(hostname, "localhost")))
+        return localhost;
+
+    if(unlikely(!hash)) hash = simple_hash(hostname);
+
+    rrd_rdlock();
+    RRDHOST *host;
+    rrdhost_foreach_read(host) {
+        if(unlikely((hash == host->hash_hostname && !strcmp(hostname, host->hostname)))) {
+            rrd_unlock();
+            return host;
+        }
+    }
+    rrd_unlock();
+
+    return NULL;
+}
+
+#define rrdhost_index_add(rrdhost) (RRDHOST *)avl_insert_lock(&(rrdhost_root_index), (avl *)(rrdhost))
+#define rrdhost_index_del(rrdhost) (RRDHOST *)avl_remove_lock(&(rrdhost_root_index), (avl *)(rrdhost))
+
+
+// ----------------------------------------------------------------------------
+// RRDHOST - internal helpers
+
+static inline void rrdhost_init_hostname(RRDHOST *host, const char *hostname) {
+    freez(host->hostname);
+    host->hostname = strdupz(hostname);
+    host->hash_hostname = simple_hash(host->hostname);
+}
+
+static inline void rrdhost_init_os(RRDHOST *host, const char *os) {
+    freez(host->os);
+    host->os = strdupz(os?os:"unknown");
+}
+
+static inline void rrdhost_init_machine_guid(RRDHOST *host, const char *machine_guid) {
+    strncpy(host->machine_guid, machine_guid, GUID_LEN);
+    host->machine_guid[GUID_LEN] = '\0';
+    host->hash_machine_guid = simple_hash(host->machine_guid);
+}
+
+
+// ----------------------------------------------------------------------------
+// RRDHOST - add a host
+
+RRDHOST *rrdhost_create(const char *hostname,
+        const char *guid,
+        const char *os,
+        int update_every,
+        int entries,
+        RRD_MEMORY_MODE memory_mode,
+        int health_enabled,
+        int rrdpush_enabled,
+        char *rrdpush_destination,
+        char *rrdpush_api_key,
+        int is_localhost
+) {
+
+    debug(D_RRDHOST, "Host '%s': adding with guid '%s'", hostname, guid);
+
+    RRDHOST *host = callocz(1, sizeof(RRDHOST));
+
+    host->rrd_update_every    = update_every;
+    host->rrd_history_entries = entries;
+    host->rrd_memory_mode     = memory_mode;
+    host->health_enabled      = (memory_mode == RRD_MEMORY_MODE_NONE)? 0 : health_enabled;
+    host->rrdpush_enabled     = (rrdpush_enabled && rrdpush_destination && *rrdpush_destination && rrdpush_api_key && *rrdpush_api_key);
+    host->rrdpush_destination = (host->rrdpush_enabled)?strdupz(rrdpush_destination):NULL;
+    host->rrdpush_api_key     = (host->rrdpush_enabled)?strdupz(rrdpush_api_key):NULL;
+
+    host->rrdpush_pipe[0] = -1;
+    host->rrdpush_pipe[1] = -1;
+    host->rrdpush_socket  = -1;
+
+    pthread_mutex_init(&host->rrdpush_mutex, NULL);
+    pthread_rwlock_init(&host->rrdhost_rwlock, NULL);
+
+    rrdhost_init_hostname(host, hostname);
+    rrdhost_init_machine_guid(host, guid);
+    rrdhost_init_os(host, os);
+
+    avl_init_lock(&(host->rrdset_root_index),      rrdset_compare);
+    avl_init_lock(&(host->rrdset_root_index_name), rrdset_compare_name);
+    avl_init_lock(&(host->rrdfamily_root_index),   rrdfamily_compare);
+    avl_init_lock(&(host->variables_root_index),   rrdvar_compare);
+
+    // ------------------------------------------------------------------------
+    // initialize health variables
+
+    host->health_log.next_log_id = 1;
+    host->health_log.next_alarm_id = 1;
+    host->health_log.max = 1000;
+    host->health_log.next_log_id =
+    host->health_log.next_alarm_id = (uint32_t)now_realtime_sec();
+
+    long n = config_get_number(CONFIG_SECTION_HEALTH, "in memory max health log entries", host->health_log.max);
+    if(n < 10) {
+        error("Host '%s': health configuration has invalid max log entries %ld. Using default %u", host->hostname, n, host->health_log.max);
+        config_set_number(CONFIG_SECTION_HEALTH, "in memory max health log entries", (long)host->health_log.max);
+    }
+    else
+        host->health_log.max = (unsigned int)n;
+
+    pthread_rwlock_init(&(host->health_log.alarm_log_rwlock), NULL);
+
+    char filename[FILENAME_MAX + 1];
+
+    if(is_localhost) {
+
+        host->cache_dir  = strdupz(netdata_configured_cache_dir);
+        host->varlib_dir = strdupz(netdata_configured_varlib_dir);
+
+    }
+    else {
+        // this is not localhost - append our GUID to localhost path
+
+        snprintfz(filename, FILENAME_MAX, "%s/%s", netdata_configured_cache_dir, host->machine_guid);
+        host->cache_dir = strdupz(filename);
+
+        if(host->rrd_memory_mode == RRD_MEMORY_MODE_MAP || host->rrd_memory_mode == RRD_MEMORY_MODE_SAVE) {
+            int r = mkdir(host->cache_dir, 0775);
+            if(r != 0 && errno != EEXIST)
+                error("Host '%s': cannot create directory '%s'", host->hostname, host->cache_dir);
+        }
+
+        snprintfz(filename, FILENAME_MAX, "%s/%s", netdata_configured_varlib_dir, host->machine_guid);
+        host->varlib_dir = strdupz(filename);
+
+        if(host->health_enabled) {
+            int r = mkdir(host->varlib_dir, 0775);
+            if(r != 0 && errno != EEXIST)
+                error("Host '%s': cannot create directory '%s'", host->hostname, host->varlib_dir);
+       }
+
+    }
+
+    if(host->health_enabled) {
+        snprintfz(filename, FILENAME_MAX, "%s/health", host->varlib_dir);
+        int r = mkdir(filename, 0775);
+        if(r != 0 && errno != EEXIST)
+            error("Host '%s': cannot create directory '%s'", host->hostname, filename);
+    }
+
+    snprintfz(filename, FILENAME_MAX, "%s/health/health-log.db", host->varlib_dir);
+    host->health_log_filename = strdupz(filename);
+
+    snprintfz(filename, FILENAME_MAX, "%s/alarm-notify.sh", netdata_configured_plugins_dir);
+    host->health_default_exec = strdupz(config_get(CONFIG_SECTION_HEALTH, "script to execute on alarm", filename));
+    host->health_default_recipient = strdup("root");
+
+
+    // ------------------------------------------------------------------------
+    // load health configuration
+
+    if(host->health_enabled) {
+        health_alarm_log_load(host);
+        health_alarm_log_open(host);
+
+        rrdhost_wrlock(host);
+        health_readdir(host, health_config_dir());
+        rrdhost_unlock(host);
+    }
+
+
+    // ------------------------------------------------------------------------
+    // link it and add it to the index
+
+    rrd_wrlock();
+
+    if(is_localhost) {
+        host->next = localhost;
+        localhost = host;
+    }
+    else {
+        if(localhost) {
+            host->next = localhost->next;
+            localhost->next = host;
+        }
+        else localhost = host;
+    }
+
+    RRDHOST *t = rrdhost_index_add(host);
+
+    if(t != host) {
+        error("Host '%s': cannot add host with machine guid '%s' to index. It already exists as host '%s' with machine guid '%s'.", host->hostname, host->machine_guid, t->hostname, t->machine_guid);
+        rrdhost_free(host);
+        host = NULL;
+    }
+    else {
+        info("Host '%s' with guid '%s' initialized"
+                     ", os %s"
+                     ", update every %d"
+                     ", memory mode %s"
+                     ", history entries %d"
+                     ", streaming %s"
+                     " (to '%s' with api key '%s')"
+                     ", health %s"
+                     ", cache_dir '%s'"
+                     ", varlib_dir '%s'"
+                     ", health_log '%s'"
+                     ", alarms default handler '%s'"
+                     ", alarms default recipient '%s'"
+             , host->hostname
+             , host->machine_guid
+             , host->os
+             , host->rrd_update_every
+             , rrd_memory_mode_name(host->rrd_memory_mode)
+             , host->rrd_history_entries
+             , host->rrdpush_enabled?"enabled":"disabled"
+             , host->rrdpush_destination?host->rrdpush_destination:""
+             , host->rrdpush_api_key?host->rrdpush_api_key:""
+             , host->health_enabled?"enabled":"disabled"
+             , host->cache_dir
+             , host->varlib_dir
+             , host->health_log_filename
+             , host->health_default_exec
+             , host->health_default_recipient
+        );
+    }
+
+    rrd_hosts_available++;
+    rrd_unlock();
+
+    return host;
+}
+
+RRDHOST *rrdhost_find_or_create(
+          const char *hostname
+        , const char *guid
+        , const char *os
+        , int update_every
+        , int history
+        , RRD_MEMORY_MODE mode
+        , int health_enabled
+        , int rrdpush_enabled
+        , char *rrdpush_destination
+        , char *rrdpush_api_key
+) {
+    debug(D_RRDHOST, "Searching for host '%s' with guid '%s'", hostname, guid);
+
+    RRDHOST *host = rrdhost_find_by_guid(guid, 0);
+    if(!host) {
+        host = rrdhost_create(
+                hostname
+                , guid
+                , os
+                , update_every
+                , history
+                , mode
+                , health_enabled
+                , rrdpush_enabled
+                , rrdpush_destination
+                , rrdpush_api_key
+                , 0
+        );
+    }
+    else {
+        host->health_enabled = health_enabled;
+
+        if(strcmp(host->hostname, hostname)) {
+            char *t = host->hostname;
+            char *n = strdupz(hostname);
+            host->hostname = n;
+            freez(t);
+        }
+
+        if(host->rrd_update_every != update_every)
+            error("Host '%s' has an update frequency of %d seconds, but the wanted one is %d seconds.", host->hostname, host->rrd_update_every, update_every);
+
+        if(host->rrd_history_entries != history)
+            error("Host '%s' has history of %d entries, but the wanted one is %d entries.", host->hostname, host->rrd_history_entries, history);
+
+        if(host->rrd_memory_mode != mode)
+            error("Host '%s' has memory mode '%s', but the wanted one is '%s'.", host->hostname, rrd_memory_mode_name(host->rrd_memory_mode), rrd_memory_mode_name(mode));
+    }
+
+    rrdhost_cleanup_remote_stale(host);
+
+    return host;
+}
+
+void rrdhost_cleanup_remote_stale(RRDHOST *protected) {
+    rrd_wrlock();
+
+    RRDHOST *h;
+    rrdhost_foreach_write(h) {
+        if(h != protected
+           && h != localhost
+           && !h->connected_senders
+           && h->senders_disconnected_time + rrdhost_free_orphan_time > now_realtime_sec()) {
+            info("Host '%s' with machine guid '%s' is obsolete - cleaning up.", h->hostname, h->machine_guid);
+            rrdhost_save(h);
+            rrdhost_free(h);
+            break;
+        }
+    }
+
+    rrd_unlock();
+}
+
+// ----------------------------------------------------------------------------
+// RRDHOST global / startup initialization
+
+void rrd_init(char *hostname) {
+    health_init();
+    registry_init();
+    rrdpush_init();
+
+    debug(D_RRDHOST, "Initializing localhost with hostname '%s'", hostname);
+    localhost = rrdhost_create(
+            hostname
+            , registry_get_this_machine_guid()
+            , os_type
+            , default_rrd_update_every
+            , default_rrd_history_entries
+            , default_rrd_memory_mode
+            , default_health_enabled
+            , default_rrdpush_enabled
+            , default_rrdpush_destination
+            , default_rrdpush_api_key
+            , 1
+    );
+}
+
+// ----------------------------------------------------------------------------
+// RRDHOST - lock validations
+// there are only used when NETDATA_INTERNAL_CHECKS is set
+
+void rrdhost_check_rdlock_int(RRDHOST *host, const char *file, const char *function, const unsigned long line) {
+    debug(D_RRDHOST, "Checking read lock on host '%s'", host->hostname);
+
+    int ret = pthread_rwlock_trywrlock(&host->rrdhost_rwlock);
+    if(ret == 0)
+        fatal("RRDHOST '%s' should be read-locked, but it is not, at function %s() at line %lu of file '%s'", host->hostname, function, line, file);
+}
+
+void rrdhost_check_wrlock_int(RRDHOST *host, const char *file, const char *function, const unsigned long line) {
+    debug(D_RRDHOST, "Checking write lock on host '%s'", host->hostname);
+
+    int ret = pthread_rwlock_tryrdlock(&host->rrdhost_rwlock);
+    if(ret == 0)
+        fatal("RRDHOST '%s' should be write-locked, but it is not, at function %s() at line %lu of file '%s'", host->hostname, function, line, file);
+}
+
+void rrd_check_rdlock_int(const char *file, const char *function, const unsigned long line) {
+    debug(D_RRDHOST, "Checking read lock on all RRDs");
+
+    int ret = pthread_rwlock_trywrlock(&rrd_rwlock);
+    if(ret == 0)
+        fatal("RRDs should be read-locked, but it are not, at function %s() at line %lu of file '%s'", function, line, file);
+}
+
+void rrd_check_wrlock_int(const char *file, const char *function, const unsigned long line) {
+    debug(D_RRDHOST, "Checking write lock on all RRDs");
+
+    int ret = pthread_rwlock_tryrdlock(&rrd_rwlock);
+    if(ret == 0)
+        fatal("RRDs should be write-locked, but it are not, at function %s() at line %lu of file '%s'", function, line, file);
+}
+
+// ----------------------------------------------------------------------------
+// RRDHOST - free
+
+void rrdhost_free(RRDHOST *host) {
+    if(!host) return;
+
+    info("Freeing all memory for host '%s'...", host->hostname);
+
+    rrd_check_wrlock();     // make sure the RRDs are write locked
+    rrdhost_wrlock(host);   // lock this RRDHOST
+
+    // ------------------------------------------------------------------------
+    // release its children resources
+
+    while(host->rrdset_root) rrdset_free(host->rrdset_root);
+
+    while(host->alarms) rrdcalc_free(host, host->alarms);
+    while(host->templates) rrdcalctemplate_free(host, host->templates);
+    health_alarm_log_free(host);
+
+
+    // ------------------------------------------------------------------------
+    // remove it from the indexes
+
+    if(rrdhost_index_del(host) != host)
+        error("RRDHOST '%s' removed from index, deleted the wrong entry.", host->hostname);
+
+
+    // ------------------------------------------------------------------------
+    // unlink it from the host
+
+    if(host == localhost) {
+        localhost = host->next;
+    }
+    else {
+        // find the previous one
+        RRDHOST *h;
+        for(h = localhost; h && h->next != host ; h = h->next) ;
+
+        // bypass it
+        if(h) h->next = host->next;
+        else error("Request to free RRDHOST '%s': cannot find it", host->hostname);
+    }
+
+    // ------------------------------------------------------------------------
+    // free it
+
+    rrdpush_sender_thread_stop(host);
+
+    freez(host->os);
+    freez(host->cache_dir);
+    freez(host->varlib_dir);
+    freez(host->rrdpush_api_key);
+    freez(host->rrdpush_destination);
+    freez(host->health_default_exec);
+    freez(host->health_default_recipient);
+    freez(host->health_log_filename);
+    freez(host->hostname);
+    rrdhost_unlock(host);
+    freez(host);
+
+    rrd_hosts_available--;
+}
+
+void rrdhost_free_all(void) {
+    rrd_wrlock();
+    while(localhost) rrdhost_free(localhost);
+    rrd_unlock();
+}
+
+// ----------------------------------------------------------------------------
+// RRDHOST - save
+
+void rrdhost_save(RRDHOST *host) {
+    if(!host) return;
+
+    info("Saving database of host '%s'...", host->hostname);
+
+    RRDSET *st;
+    RRDDIM *rd;
+
+    // we get a write lock
+    // to ensure only one thread is saving the database
+    rrdhost_wrlock(host);
+
+    rrdset_foreach_write(st, host) {
+        rrdset_rdlock(st);
+
+        if(st->rrd_memory_mode == RRD_MEMORY_MODE_SAVE) {
+            debug(D_RRD_STATS, "Saving stats '%s' to '%s'.", st->name, st->cache_filename);
+            savememory(st->cache_filename, st, st->memsize);
+        }
+
+        rrddim_foreach_read(rd, st) {
+            if(likely(rd->rrd_memory_mode == RRD_MEMORY_MODE_SAVE)) {
+                debug(D_RRD_STATS, "Saving dimension '%s' to '%s'.", rd->name, rd->cache_filename);
+                savememory(rd->cache_filename, rd, rd->memsize);
+            }
+        }
+
+        rrdset_unlock(st);
+    }
+
+    rrdhost_unlock(host);
+}
+
+void rrdhost_save_all(void) {
+    info("Saving database [%zu hosts(s)]...", rrd_hosts_available);
+
+    rrd_rdlock();
+
+    RRDHOST *host;
+    rrdhost_foreach_read(host)
+        rrdhost_save(host);
+
+    rrd_unlock();
+}
diff --git a/src/rrdpush.c b/src/rrdpush.c
new file mode 100644 (file)
index 0000000..06695c6
--- /dev/null
@@ -0,0 +1,733 @@
+#include "common.h"
+
+/*
+ * rrdpush
+ *
+ * 3 threads are involved for all stream operations
+ *
+ * 1. a random data collection thread, calling rrdset_done_push()
+ *    this is called for each chart.
+ *
+ *    the output of this work is kept in a BUFFER in RRDHOST
+ *    the sender thread is signalled via a pipe (also in RRDHOST)
+ *
+ * 2. a sender thread running at the sending netdata
+ *    this is spawned automatically on the first chart to be pushed
+ *
+ *    It tries to push the metrics to the remote netdata, as fast
+ *    as possible (i.e. immediately after they are collected).
+ *
+ * 3. a receiver thread, running at the receiving netdata
+ *    this is spawned automatically when the sender connects to
+ *    the receiver.
+ *
+ */
+
+#define START_STREAMING_PROMPT "Hit me baby, push them over..."
+
+int default_rrdpush_enabled = 0;
+char *default_rrdpush_destination = NULL;
+char *default_rrdpush_api_key = NULL;
+
+int rrdpush_init() {
+    default_rrdpush_enabled     = appconfig_get_boolean(&stream_config, CONFIG_SECTION_STREAM, "enabled", default_rrdpush_enabled);
+    default_rrdpush_destination = appconfig_get(&stream_config, CONFIG_SECTION_STREAM, "destination", "");
+    default_rrdpush_api_key     = appconfig_get(&stream_config, CONFIG_SECTION_STREAM, "api key", "");
+    rrdhost_free_orphan_time    = appconfig_get_number(&stream_config, CONFIG_SECTION_STREAM, "free orphan hosts after seconds", rrdhost_free_orphan_time);
+
+    if(default_rrdpush_enabled && (!default_rrdpush_destination || !*default_rrdpush_destination || !default_rrdpush_api_key || !*default_rrdpush_api_key)) {
+        error("STREAM [send]: cannot enable sending thread - information is missing.");
+        default_rrdpush_enabled = 0;
+    }
+
+    return default_rrdpush_enabled;
+}
+
+#define CONNECTED_TO_SIZE 100
+
+// data collection happens from multiple threads
+// each of these threads calls rrdset_done()
+// which in turn calls rrdset_done_push()
+// which uses this pipe to notify the streaming thread
+// that there are more data ready to be sent
+#define PIPE_READ 0
+#define PIPE_WRITE 1
+
+// to have the remote netdata re-sync the charts
+// to its current clock, we send for this many
+// iterations a BEGIN line without microseconds
+// this is for the first iterations of each chart
+static unsigned int remote_clock_resync_iterations = 60;
+
+#define rrdpush_lock(host) pthread_mutex_lock(&((host)->rrdpush_mutex))
+#define rrdpush_unlock(host) pthread_mutex_unlock(&((host)->rrdpush_mutex))
+
+// checks if the current chart definition has been sent
+static inline int need_to_send_chart_definition(RRDSET *st) {
+    RRDDIM *rd;
+    rrddim_foreach_read(rd, st)
+        if(!rd->exposed)
+            return 1;
+
+    return 0;
+}
+
+// sends the current chart definition
+static inline void send_chart_definition(RRDSET *st) {
+    buffer_sprintf(st->rrdhost->rrdpush_buffer, "CHART '%s' '%s' '%s' '%s' '%s' '%s' '%s' %ld %d\n"
+                , st->id
+                , st->name
+                , st->title
+                , st->units
+                , st->family
+                , st->context
+                , rrdset_type_name(st->chart_type)
+                , st->priority
+                , st->update_every
+    );
+
+    RRDDIM *rd;
+    rrddim_foreach_read(rd, st) {
+        buffer_sprintf(st->rrdhost->rrdpush_buffer, "DIMENSION '%s' '%s' '%s' " COLLECTED_NUMBER_FORMAT " " COLLECTED_NUMBER_FORMAT " '%s %s'\n"
+                       , rd->id
+                       , rd->name
+                       , rrd_algorithm_name(rd->algorithm)
+                       , rd->multiplier
+                       , rd->divisor
+                       , rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)?"hidden":""
+                       , rrddim_flag_check(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS)?"noreset":""
+        );
+        rd->exposed = 1;
+    }
+}
+
+// sends the current chart dimensions
+static inline void send_chart_metrics(RRDSET *st) {
+    buffer_sprintf(st->rrdhost->rrdpush_buffer, "BEGIN %s %llu\n", st->id, (st->counter_done > remote_clock_resync_iterations)?st->usec_since_last_update:0);
+
+    RRDDIM *rd;
+    rrddim_foreach_read(rd, st) {
+        if(rd->updated && rd->exposed)
+            buffer_sprintf(st->rrdhost->rrdpush_buffer, "SET %s = " COLLECTED_NUMBER_FORMAT "\n"
+                       , rd->id
+                       , rd->collected_value
+        );
+    }
+
+    buffer_strcat(st->rrdhost->rrdpush_buffer, "END\n");
+}
+
+void rrdpush_sender_thread_spawn(RRDHOST *host);
+
+void rrdset_done_push(RRDSET *st) {
+    RRDHOST *host = st->rrdhost;
+
+    if(unlikely(!rrdset_flag_check(st, RRDSET_FLAG_ENABLED)))
+        return;
+
+    rrdpush_lock(host);
+
+    if(unlikely(host->rrdpush_enabled && !host->rrdpush_spawn))
+        rrdpush_sender_thread_spawn(host);
+
+    if(unlikely(!host->rrdpush_buffer || !host->rrdpush_connected)) {
+        if(unlikely(!host->rrdpush_error_shown))
+            error("STREAM %s [send]: not ready - discarding collected metrics.", host->hostname);
+
+        host->rrdpush_error_shown = 1;
+
+        rrdpush_unlock(host);
+        return;
+    }
+    else if(unlikely(host->rrdpush_error_shown)) {
+        info("STREAM %s [send]: ready - sending metrics...", host->hostname);
+        host->rrdpush_error_shown = 0;
+    }
+
+    if(need_to_send_chart_definition(st))
+        send_chart_definition(st);
+
+    send_chart_metrics(st);
+
+    // signal the sender there are more data
+    if(write(host->rrdpush_pipe[PIPE_WRITE], " ", 1) == -1)
+        error("STREAM %s [send]: cannot write to internal pipe", host->hostname);
+
+    rrdpush_unlock(host);
+}
+
+// ----------------------------------------------------------------------------
+// rrdpush sender thread
+
+// resets all the chart, so that their definitions
+// will be resent to the central netdata
+static void rrdpush_sender_thread_reset_all_charts(RRDHOST *host) {
+    rrdhost_rdlock(host);
+
+    RRDSET *st;
+    rrdset_foreach_read(st, host) {
+
+        // make it re-align the current time
+        // on the remote host
+        st->counter_done = 0;
+
+        rrdset_rdlock(st);
+
+        RRDDIM *rd;
+        rrddim_foreach_read(rd, st)
+            rd->exposed = 0;
+
+        rrdset_unlock(st);
+    }
+
+    rrdhost_unlock(host);
+}
+
+static inline void rrdpush_sender_thread_data_flush(RRDHOST *host) {
+    rrdpush_lock(host);
+    if(buffer_strlen(host->rrdpush_buffer))
+        error("STREAM %s [send]: discarding %zu bytes of metrics already in the buffer.", host->hostname, buffer_strlen(host->rrdpush_buffer));
+
+    buffer_flush(host->rrdpush_buffer);
+    rrdpush_sender_thread_reset_all_charts(host);
+    rrdpush_unlock(host);
+}
+
+static inline void rrdpush_sender_thread_lock(RRDHOST *host) {
+    if(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL) != 0)
+        error("STREAM %s [send]: cannot set pthread cancel state to DISABLE.", host->hostname);
+
+    rrdpush_lock(host);
+}
+
+static inline void rrdpush_sender_thread_unlock(RRDHOST *host) {
+    if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+        error("STREAM %s [send]: cannot set pthread cancel state to DISABLE.", host->hostname);
+
+    rrdpush_unlock(host);
+}
+
+static void rrdpush_sender_thread_cleanup(RRDHOST *host) {
+    rrdpush_lock(host);
+
+    host->rrdpush_connected = 0;
+
+    if(host->rrdpush_socket != -1) {
+        close(host->rrdpush_socket);
+        host->rrdpush_socket = -1;
+    }
+
+    // close the pipe
+    if(host->rrdpush_pipe[PIPE_READ] != -1) {
+        close(host->rrdpush_pipe[PIPE_READ]);
+        host->rrdpush_pipe[PIPE_READ] = -1;
+    }
+
+    if(host->rrdpush_pipe[PIPE_WRITE] != -1) {
+        close(host->rrdpush_pipe[PIPE_WRITE]);
+        host->rrdpush_pipe[PIPE_WRITE] = -1;
+    }
+
+    buffer_free(host->rrdpush_buffer);
+    host->rrdpush_buffer = NULL;
+
+    host->rrdpush_spawn = 0;
+
+    rrdpush_unlock(host);
+}
+
+void rrdpush_sender_thread_stop(RRDHOST *host) {
+    rrdhost_check_wrlock(host);
+
+    if(host->rrdpush_spawn) {
+        info("STREAM %s [send]: stopping sending thread...", host->hostname);
+        pthread_cancel(host->rrdpush_thread);
+        rrdpush_sender_thread_cleanup(host);
+    }
+}
+
+void *rrdpush_sender_thread(void *ptr) {
+    RRDHOST *host = (RRDHOST *)ptr;
+
+    info("STREAM %s [send]: thread created (task id %d)", host->hostname, gettid());
+
+    if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+        error("STREAM %s [send]: cannot set pthread cancel type to DEFERRED.", host->hostname);
+
+    if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+        error("STREAM %s [send]: cannot set pthread cancel state to ENABLE.", host->hostname);
+
+    int timeout = (int)appconfig_get_number(&stream_config, CONFIG_SECTION_STREAM, "timeout seconds", 60);
+    int default_port = (int)appconfig_get_number(&stream_config, CONFIG_SECTION_STREAM, "default port", 19999);
+    size_t max_size = (size_t)appconfig_get_number(&stream_config, CONFIG_SECTION_STREAM, "buffer size bytes", 1024 * 1024);
+    unsigned int reconnect_delay = (unsigned int)appconfig_get_number(&stream_config, CONFIG_SECTION_STREAM, "reconnect delay seconds", 5);
+    remote_clock_resync_iterations = (unsigned int)appconfig_get_number(&stream_config, CONFIG_SECTION_STREAM, "initial clock resync iterations", remote_clock_resync_iterations);
+    char connected_to[CONNECTED_TO_SIZE + 1] = "";
+
+    if(!host->rrdpush_enabled || !host->rrdpush_destination || !*host->rrdpush_destination || !host->rrdpush_api_key || !*host->rrdpush_api_key)
+        goto cleanup;
+
+    // initialize rrdpush globals
+    host->rrdpush_buffer = buffer_create(1);
+    host->rrdpush_connected = 0;
+    if(pipe(host->rrdpush_pipe) == -1) fatal("STREAM %s [send]: cannot create required pipe.", host->hostname);
+
+    // initialize local variables
+    size_t begin = 0;
+    size_t reconnects_counter = 0;
+    size_t sent_bytes = 0;
+    size_t sent_connection = 0;
+
+    struct timeval tv = {
+            .tv_sec = timeout,
+            .tv_usec = 0
+    };
+
+    struct pollfd fds[2], *ifd, *ofd;
+    nfds_t fdmax;
+
+    ifd = &fds[0];
+    ofd = &fds[1];
+
+    for(; host->rrdpush_enabled && !netdata_exit ;) {
+
+        if(unlikely(host->rrdpush_socket == -1)) {
+            // stop appending data into rrdpush_buffer
+            // they will be lost, so there is no point to do it
+            host->rrdpush_connected = 0;
+
+            info("STREAM %s [send to %s]: connecting...", host->hostname, host->rrdpush_destination);
+            host->rrdpush_socket = connect_to_one_of(host->rrdpush_destination, default_port, &tv, &reconnects_counter, connected_to, CONNECTED_TO_SIZE);
+
+            if(unlikely(host->rrdpush_socket == -1)) {
+                error("STREAM %s [send to %s]: failed to connect", host->hostname, host->rrdpush_destination);
+                sleep(reconnect_delay);
+                continue;
+            }
+
+            info("STREAM %s [send to %s]: initializing communication...", host->hostname, connected_to);
+
+            char http[1000 + 1];
+            snprintfz(http, 1000,
+                    "STREAM key=%s&hostname=%s&machine_guid=%s&os=%s&update_every=%d HTTP/1.1\r\n"
+                    "User-Agent: netdata-push-service/%s\r\n"
+                    "Accept: */*\r\n\r\n"
+                      , host->rrdpush_api_key
+                      , host->hostname
+                      , host->machine_guid
+                      , host->os
+                      , default_rrd_update_every
+                      , program_version
+            );
+
+            if(send_timeout(host->rrdpush_socket, http, strlen(http), 0, timeout) == -1) {
+                close(host->rrdpush_socket);
+                host->rrdpush_socket = -1;
+                error("STREAM %s [send to %s]: failed to send http header to netdata", host->hostname, connected_to);
+                sleep(reconnect_delay);
+                continue;
+            }
+
+            info("STREAM %s [send to %s]: waiting response from remote netdata...", host->hostname, connected_to);
+
+            if(recv_timeout(host->rrdpush_socket, http, 1000, 0, timeout) == -1) {
+                close(host->rrdpush_socket);
+                host->rrdpush_socket = -1;
+                error("STREAM %s [send to %s]: failed to initialize communication", host->hostname, connected_to);
+                sleep(reconnect_delay);
+                continue;
+            }
+
+            if(strncmp(http, START_STREAMING_PROMPT, strlen(START_STREAMING_PROMPT))) {
+                close(host->rrdpush_socket);
+                host->rrdpush_socket = -1;
+                error("STREAM %s [send to %s]: server is not replying properly.", host->hostname, connected_to);
+                sleep(reconnect_delay);
+                continue;
+            }
+
+            info("STREAM %s [send to %s]: established communication - sending metrics...", host->hostname, connected_to);
+
+            if(fcntl(host->rrdpush_socket, F_SETFL, O_NONBLOCK) < 0)
+                error("STREAM %s [send to %s]: cannot set non-blocking mode for socket.", host->hostname, connected_to);
+
+            rrdpush_sender_thread_data_flush(host);
+            sent_connection = 0;
+
+            // allow appending data into rrdpush_buffer
+            host->rrdpush_connected = 1;
+        }
+
+        ifd->fd = host->rrdpush_pipe[PIPE_READ];
+        ifd->events = POLLIN;
+        ifd->revents = 0;
+
+        ofd->fd = host->rrdpush_socket;
+        ofd->revents = 0;
+        if(begin < buffer_strlen(host->rrdpush_buffer)) {
+            ofd->events = POLLOUT;
+            fdmax = 2;
+        }
+        else {
+            ofd->events = 0;
+            fdmax = 1;
+        }
+
+        if(netdata_exit) break;
+        int retval = poll(fds, fdmax, timeout * 1000);
+        if(netdata_exit) break;
+
+        if(unlikely(retval == -1)) {
+            if(errno == EAGAIN || errno == EINTR)
+                continue;
+
+            error("STREAM %s [send to %s]: failed to poll().", host->hostname, connected_to);
+            close(host->rrdpush_socket);
+            host->rrdpush_socket = -1;
+            break;
+        }
+        else if(unlikely(!retval)) {
+            // timeout
+            continue;
+        }
+
+        if(ifd->revents & POLLIN) {
+            char buffer[1000 + 1];
+            if(read(host->rrdpush_pipe[PIPE_READ], buffer, 1000) == -1)
+                error("STREAM %s [send to %s]: cannot read from internal pipe.", host->hostname, connected_to);
+        }
+
+        if(ofd->revents & POLLOUT && begin < buffer_strlen(host->rrdpush_buffer)) {
+            rrdpush_sender_thread_lock(host);
+            ssize_t ret = send(host->rrdpush_socket, &host->rrdpush_buffer->buffer[begin], buffer_strlen(host->rrdpush_buffer) - begin, MSG_DONTWAIT);
+            if(ret == -1) {
+                if(errno != EAGAIN && errno != EINTR) {
+                    error("STREAM %s [send to %s]: failed to send metrics - closing connection - we have sent %zu bytes on this connection.", host->hostname, connected_to, sent_connection);
+                    close(host->rrdpush_socket);
+                    host->rrdpush_socket = -1;
+                }
+            }
+            else {
+                sent_connection += ret;
+                sent_bytes += ret;
+                begin += ret;
+                if(begin == buffer_strlen(host->rrdpush_buffer)) {
+                    buffer_flush(host->rrdpush_buffer);
+                    begin = 0;
+                }
+            }
+            rrdpush_sender_thread_unlock(host);
+        }
+
+        // protection from overflow
+        if(host->rrdpush_buffer->len > max_size) {
+            errno = 0;
+            error("STREAM %s [send to %s]: too many data pending - buffer is %zu bytes long, %zu unsent - we have sent %zu bytes in total, %zu on this connection. Closing connection to flush the data.", host->hostname, connected_to, host->rrdpush_buffer->len, host->rrdpush_buffer->len - begin, sent_bytes, sent_connection);
+            if(host->rrdpush_socket != -1) {
+                close(host->rrdpush_socket);
+                host->rrdpush_socket = -1;
+            }
+        }
+    }
+
+cleanup:
+    debug(D_WEB_CLIENT, "STREAM %s [send]: sending thread exits.", host->hostname);
+
+    rrdpush_sender_thread_cleanup(host);
+
+    pthread_exit(NULL);
+    return NULL;
+}
+
+
+// ----------------------------------------------------------------------------
+// rrdpush receiver thread
+
+int rrdpush_receive(int fd, const char *key, const char *hostname, const char *machine_guid, const char *os, int update_every, char *client_ip, char *client_port) {
+    RRDHOST *host;
+    int history = default_rrd_history_entries;
+    RRD_MEMORY_MODE mode = default_rrd_memory_mode;
+    int health_enabled = default_health_enabled;
+    int rrdpush_enabled = default_rrdpush_enabled;
+    char *rrdpush_destination = default_rrdpush_destination;
+    char *rrdpush_api_key = default_rrdpush_api_key;
+    time_t alarms_delay = 60;
+
+    update_every = (int)appconfig_get_number(&stream_config, machine_guid, "update every", update_every);
+    if(update_every < 0) update_every = 1;
+
+    history = (int)appconfig_get_number(&stream_config, key, "default history", history);
+    history = (int)appconfig_get_number(&stream_config, machine_guid, "history", history);
+    if(history < 5) history = 5;
+
+    mode = rrd_memory_mode_id(appconfig_get(&stream_config, key, "default memory mode", rrd_memory_mode_name(mode)));
+    mode = rrd_memory_mode_id(appconfig_get(&stream_config, machine_guid, "memory mode", rrd_memory_mode_name(mode)));
+
+    health_enabled = appconfig_get_boolean_ondemand(&stream_config, key, "health enabled by default", health_enabled);
+    health_enabled = appconfig_get_boolean_ondemand(&stream_config, machine_guid, "health enabled", health_enabled);
+
+    alarms_delay = appconfig_get_number(&stream_config, key, "default postpone alarms on connect seconds", alarms_delay);
+    alarms_delay = appconfig_get_number(&stream_config, machine_guid, "postpone alarms on connect seconds", alarms_delay);
+
+    rrdpush_enabled = appconfig_get_boolean(&stream_config, key, "default proxy enabled", rrdpush_enabled);
+    rrdpush_enabled = appconfig_get_boolean(&stream_config, machine_guid, "proxy enabled", rrdpush_enabled);
+
+    rrdpush_destination = appconfig_get(&stream_config, key, "default proxy destination", rrdpush_destination);
+    rrdpush_destination = appconfig_get(&stream_config, machine_guid, "proxy destination", rrdpush_destination);
+
+    rrdpush_api_key = appconfig_get(&stream_config, key, "default proxy api key", rrdpush_api_key);
+    rrdpush_api_key = appconfig_get(&stream_config, machine_guid, "proxy api key", rrdpush_api_key);
+
+    if(!strcmp(machine_guid, "localhost"))
+        host = localhost;
+    else
+        host = rrdhost_find_or_create(
+                hostname
+                , machine_guid
+                , os
+                , update_every
+                , history
+                , mode
+                , (health_enabled != CONFIG_BOOLEAN_NO)
+                , (rrdpush_enabled && rrdpush_destination && *rrdpush_destination && rrdpush_api_key && *rrdpush_api_key)
+                , rrdpush_destination
+                , rrdpush_api_key
+        );
+
+    if(!host) {
+        close(fd);
+        error("STREAM %s [receive from [%s]:%s]: failed to find/create host structure.", hostname, client_ip, client_port);
+        return 1;
+    }
+
+#ifdef NETDATA_INTERNAL_CHECKS
+    info("STREAM %s [receive from [%s]:%s]: client willing to stream metrics for host '%s' with machine_guid '%s': update every = %d, history = %d, memory mode = %s, health %s"
+         , hostname
+         , client_ip
+         , client_port
+         , host->hostname
+         , host->machine_guid
+         , host->rrd_update_every
+         , host->rrd_history_entries
+         , rrd_memory_mode_name(host->rrd_memory_mode)
+         , (health_enabled == CONFIG_BOOLEAN_NO)?"disabled":((health_enabled == CONFIG_BOOLEAN_YES)?"enabled":"auto")
+    );
+#endif // NETDATA_INTERNAL_CHECKS
+
+    struct plugind cd = {
+            .enabled = 1,
+            .update_every = default_rrd_update_every,
+            .pid = 0,
+            .serial_failures = 0,
+            .successful_collections = 0,
+            .obsolete = 0,
+            .started_t = now_realtime_sec(),
+            .next = NULL,
+    };
+
+    // put the client IP and port into the buffers used by plugins.d
+    snprintfz(cd.id,           CONFIG_MAX_NAME,  "%s:%s", client_ip, client_port);
+    snprintfz(cd.filename,     FILENAME_MAX,     "%s:%s", client_ip, client_port);
+    snprintfz(cd.fullfilename, FILENAME_MAX,     "%s:%s", client_ip, client_port);
+    snprintfz(cd.cmd,          PLUGINSD_CMD_MAX, "%s:%s", client_ip, client_port);
+
+    info("STREAM %s [receive from [%s]:%s]: initializing communication...", host->hostname, client_ip, client_port);
+    if(send_timeout(fd, START_STREAMING_PROMPT, strlen(START_STREAMING_PROMPT), 0, 60) != strlen(START_STREAMING_PROMPT)) {
+        error("STREAM %s [receive from [%s]:%s]: cannot send ready command.", host->hostname, client_ip, client_port);
+        return 0;
+    }
+
+    // remove the non-blocking flag from the socket
+    if(fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK) == -1)
+        error("STREAM %s [receive from [%s]:%s]: cannot remove the non-blocking flag from socket %d", host->hostname, client_ip, client_port, fd);
+
+    // convert the socket to a FILE *
+    FILE *fp = fdopen(fd, "r");
+    if(!fp) {
+        error("STREAM %s [receive from [%s]:%s]: failed to get a FILE for FD %d.", host->hostname, client_ip, client_port, fd);
+        return 0;
+    }
+
+    rrdhost_wrlock(host);
+    host->connected_senders++;
+    if(health_enabled != CONFIG_BOOLEAN_NO)
+        host->health_delay_up_to = now_realtime_sec() + alarms_delay;
+    rrdhost_unlock(host);
+
+    // call the plugins.d processor to receive the metrics
+    info("STREAM %s [receive from [%s]:%s]: receiving metrics...", host->hostname, client_ip, client_port);
+    size_t count = pluginsd_process(host, &cd, fp, 1);
+    error("STREAM %s [receive from [%s]:%s]: disconnected (completed updates %zu).", host->hostname, client_ip, client_port, count);
+
+    rrdhost_wrlock(host);
+    host->connected_senders--;
+    if(!host->connected_senders) {
+        if(health_enabled == CONFIG_BOOLEAN_AUTO)
+            host->health_enabled = 0;
+
+        host->senders_disconnected_time = now_realtime_sec();
+
+        rrdpush_sender_thread_stop(host);
+    }
+    rrdhost_unlock(host);
+
+    // cleanup
+    fclose(fp);
+
+    return (int)count;
+}
+
+struct rrdpush_thread {
+    int fd;
+    char *key;
+    char *hostname;
+    char *machine_guid;
+    char *os;
+    char *client_ip;
+    char *client_port;
+    int update_every;
+};
+
+void *rrdpush_receiver_thread(void *ptr) {
+    struct rrdpush_thread *rpt = (struct rrdpush_thread *)ptr;
+
+    if (pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+        error("STREAM %s [receive]: cannot set pthread cancel type to DEFERRED.", rpt->hostname);
+
+    if (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+        error("STREAM %s [receive]: cannot set pthread cancel state to ENABLE.", rpt->hostname);
+
+
+    info("STREAM %s [%s]:%s: receive thread created (task id %d)", rpt->hostname, rpt->client_ip, rpt->client_port, gettid());
+    rrdpush_receive(rpt->fd, rpt->key, rpt->hostname, rpt->machine_guid, rpt->os, rpt->update_every, rpt->client_ip, rpt->client_port);
+    info("STREAM %s [receive from [%s]:%s]: receive thread ended (task id %d)", rpt->hostname, rpt->client_ip, rpt->client_port, gettid());
+
+    close(rpt->fd);
+    freez(rpt->key);
+    freez(rpt->hostname);
+    freez(rpt->machine_guid);
+    freez(rpt->os);
+    freez(rpt->client_ip);
+    freez(rpt->client_port);
+    freez(rpt);
+
+    pthread_exit(NULL);
+    return NULL;
+}
+
+void rrdpush_sender_thread_spawn(RRDHOST *host) {
+    if(pthread_create(&host->rrdpush_thread, NULL, rrdpush_sender_thread, (void *)host))
+        error("STREAM %s [send]: failed to create new thread for client.", host->hostname);
+
+    else if(pthread_detach(host->rrdpush_thread))
+        error("STREAM %s [send]: cannot request detach newly created thread.", host->hostname);
+
+    host->rrdpush_spawn = 1;
+}
+
+int rrdpush_receiver_thread_spawn(RRDHOST *host, struct web_client *w, char *url) {
+    (void)host;
+
+    info("STREAM [receive from [%s]:%s]: new client connection.", w->client_ip, w->client_port);
+
+    char *key = NULL, *hostname = NULL, *machine_guid = NULL, *os = NULL;
+    int update_every = default_rrd_update_every;
+    char buf[GUID_LEN + 1];
+
+    while(url) {
+        char *value = mystrsep(&url, "?&");
+        if(!value || !*value) continue;
+
+        char *name = mystrsep(&value, "=");
+        if(!name || !*name) continue;
+        if(!value || !*value) continue;
+
+        if(!strcmp(name, "key"))
+            key = value;
+        else if(!strcmp(name, "hostname"))
+            hostname = value;
+        else if(!strcmp(name, "machine_guid"))
+            machine_guid = value;
+        else if(!strcmp(name, "update_every"))
+            update_every = (int)strtoul(value, NULL, 0);
+        else if(!strcmp(name, "os"))
+            os = value;
+    }
+
+    if(!key || !*key) {
+        error("STREAM [receive from [%s]:%s]: request without an API key. Forbidding access.", w->client_ip, w->client_port);
+        buffer_flush(w->response.data);
+        buffer_sprintf(w->response.data, "You need an API key for this request.");
+        return 401;
+    }
+
+    if(!hostname || !*hostname) {
+        error("STREAM [receive from [%s]:%s]: request without a hostname. Forbidding access.", w->client_ip, w->client_port);
+        buffer_flush(w->response.data);
+        buffer_sprintf(w->response.data, "You need to send a hostname too.");
+        return 400;
+    }
+
+    if(!machine_guid || !*machine_guid) {
+        error("STREAM [receive from [%s]:%s]: request without a machine GUID. Forbidding access.", w->client_ip, w->client_port);
+        buffer_flush(w->response.data);
+        buffer_sprintf(w->response.data, "You need to send a machine GUID too.");
+        return 400;
+    }
+
+    if(regenerate_guid(key, buf) == -1) {
+        error("STREAM [receive from [%s]:%s]: API key '%s' is not valid GUID. Forbidding access.", w->client_ip, w->client_port, key);
+        buffer_flush(w->response.data);
+        buffer_sprintf(w->response.data, "Your API key is invalid.");
+        return 401;
+    }
+
+    if(regenerate_guid(machine_guid, buf) == -1) {
+        error("STREAM [receive from [%s]:%s]: machine GUID '%s' is not GUID. Forbidding access.", w->client_ip, w->client_port, key);
+        buffer_flush(w->response.data);
+        buffer_sprintf(w->response.data, "Your machine GUID is invalid.");
+        return 404;
+    }
+
+    if(!appconfig_get_boolean(&stream_config, key, "enabled", 0)) {
+        error("STREAM [receive from [%s]:%s]: API key '%s' is not allowed. Forbidding access.", w->client_ip, w->client_port, machine_guid);
+        buffer_flush(w->response.data);
+        buffer_sprintf(w->response.data, "Your API key is not permitted access.");
+        return 401;
+    }
+
+    if(!appconfig_get_boolean(&stream_config, machine_guid, "enabled", 1)) {
+        error("STREAM [receive from [%s]:%s]: machine GUID '%s' is not allowed. Forbidding access.", w->client_ip, w->client_port, machine_guid);
+        buffer_flush(w->response.data);
+        buffer_sprintf(w->response.data, "Your machine guide is not permitted access.");
+        return 404;
+    }
+
+    struct rrdpush_thread *rpt = mallocz(sizeof(struct rrdpush_thread));
+    rpt->fd           = w->ifd;
+    rpt->key          = strdupz(key);
+    rpt->hostname     = strdupz(hostname);
+    rpt->machine_guid = strdupz(machine_guid);
+    rpt->os           = strdupz(os);
+    rpt->client_ip    = strdupz(w->client_ip);
+    rpt->client_port  = strdupz(w->client_port);
+    rpt->update_every = update_every;
+    pthread_t thread;
+
+    debug(D_SYSTEM, "STREAM [receive from [%s]:%s]: starting receiving thread.", w->client_ip, w->client_port);
+
+    if(pthread_create(&thread, NULL, rrdpush_receiver_thread, (void *)rpt))
+        error("STREAM [receive from [%s]:%s]: failed to create new thread for client.", w->client_ip, w->client_port);
+
+    else if(pthread_detach(thread))
+        error("STREAM [receive from [%s]:%s]: cannot request detach newly created thread.", w->client_ip, w->client_port);
+
+    // prevent the caller from closing the streaming socket
+    if(w->ifd == w->ofd)
+        w->ifd = w->ofd = -1;
+    else
+        w->ifd = -1;
+
+    buffer_flush(w->response.data);
+    return 200;
+}
diff --git a/src/rrdpush.h b/src/rrdpush.h
new file mode 100644 (file)
index 0000000..dddbe75
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef NETDATA_RRDPUSH_H
+#define NETDATA_RRDPUSH_H
+
+extern int default_rrdpush_enabled;
+extern char *default_rrdpush_destination;
+extern char *default_rrdpush_api_key;
+
+extern int rrdpush_init();
+extern void rrdset_done_push(RRDSET *st);
+extern void *rrdpush_sender_thread(void *ptr);
+
+extern int rrdpush_receiver_thread_spawn(RRDHOST *host, struct web_client *w, char *url);
+extern void rrdpush_sender_thread_stop(RRDHOST *host);
+
+#endif //NETDATA_RRDPUSH_H
diff --git a/src/rrdset.c b/src/rrdset.c
new file mode 100644 (file)
index 0000000..13d6014
--- /dev/null
@@ -0,0 +1,1234 @@
+#define NETDATA_RRD_INTERNALS 1
+#include "common.h"
+
+#define RRD_DEFAULT_GAP_INTERPOLATIONS 1
+
+void rrdset_check_rdlock_int(RRDSET *st, const char *file, const char *function, const unsigned long line) {
+    debug(D_RRD_CALLS, "Checking read lock on chart '%s'", st->id);
+
+    int ret = pthread_rwlock_trywrlock(&st->rrdset_rwlock);
+    if(ret == 0)
+        fatal("RRDSET '%s' should be read-locked, but it is not, at function %s() at line %lu of file '%s'", st->id, function, line, file);
+}
+
+void rrdset_check_wrlock_int(RRDSET *st, const char *file, const char *function, const unsigned long line) {
+    debug(D_RRD_CALLS, "Checking write lock on chart '%s'", st->id);
+
+    int ret = pthread_rwlock_tryrdlock(&st->rrdset_rwlock);
+    if(ret == 0)
+        fatal("RRDSET '%s' should be write-locked, but it is not, at function %s() at line %lu of file '%s'", st->id, function, line, file);
+}
+
+
+// ----------------------------------------------------------------------------
+// RRDSET index
+
+int rrdset_compare(void* a, void* b) {
+    if(((RRDSET *)a)->hash < ((RRDSET *)b)->hash) return -1;
+    else if(((RRDSET *)a)->hash > ((RRDSET *)b)->hash) return 1;
+    else return strcmp(((RRDSET *)a)->id, ((RRDSET *)b)->id);
+}
+
+static RRDSET *rrdset_index_find(RRDHOST *host, const char *id, uint32_t hash) {
+    RRDSET tmp;
+    strncpyz(tmp.id, id, RRD_ID_LENGTH_MAX);
+    tmp.hash = (hash)?hash:simple_hash(tmp.id);
+
+    return (RRDSET *)avl_search_lock(&(host->rrdset_root_index), (avl *) &tmp);
+}
+
+// ----------------------------------------------------------------------------
+// RRDSET name index
+
+#define rrdset_from_avlname(avlname_ptr) ((RRDSET *)((avlname_ptr) - offsetof(RRDSET, avlname)))
+
+int rrdset_compare_name(void* a, void* b) {
+    RRDSET *A = rrdset_from_avlname(a);
+    RRDSET *B = rrdset_from_avlname(b);
+
+    // fprintf(stderr, "COMPARING: %s with %s\n", A->name, B->name);
+
+    if(A->hash_name < B->hash_name) return -1;
+    else if(A->hash_name > B->hash_name) return 1;
+    else return strcmp(A->name, B->name);
+}
+
+RRDSET *rrdset_index_add_name(RRDHOST *host, RRDSET *st) {
+    void *result;
+    // fprintf(stderr, "ADDING: %s (name: %s)\n", st->id, st->name);
+    result = avl_insert_lock(&host->rrdset_root_index_name, (avl *) (&st->avlname));
+    if(result) return rrdset_from_avlname(result);
+    return NULL;
+}
+
+RRDSET *rrdset_index_del_name(RRDHOST *host, RRDSET *st) {
+    void *result;
+    // fprintf(stderr, "DELETING: %s (name: %s)\n", st->id, st->name);
+    result = (RRDSET *)avl_remove_lock(&((host)->rrdset_root_index_name), (avl *)(&st->avlname));
+    if(result) return rrdset_from_avlname(result);
+    return NULL;
+}
+
+
+// ----------------------------------------------------------------------------
+// RRDSET - find charts
+
+static inline RRDSET *rrdset_index_find_name(RRDHOST *host, const char *name, uint32_t hash) {
+    void *result = NULL;
+    RRDSET tmp;
+    tmp.name = name;
+    tmp.hash_name = (hash)?hash:simple_hash(tmp.name);
+
+    // fprintf(stderr, "SEARCHING: %s\n", name);
+    result = avl_search_lock(&host->rrdset_root_index_name, (avl *) (&(tmp.avlname)));
+    if(result) {
+        RRDSET *st = rrdset_from_avlname(result);
+        if(strcmp(st->magic, RRDSET_MAGIC))
+            error("Search for RRDSET %s returned an invalid RRDSET %s (name %s)", name, st->id, st->name);
+
+        // fprintf(stderr, "FOUND: %s\n", name);
+        return rrdset_from_avlname(result);
+    }
+    // fprintf(stderr, "NOT FOUND: %s\n", name);
+    return NULL;
+}
+
+inline RRDSET *rrdset_find(RRDHOST *host, const char *id) {
+    debug(D_RRD_CALLS, "rrdset_find() for chart '%s' in host '%s'", id, host->hostname);
+    RRDSET *st = rrdset_index_find(host, id, 0);
+    return(st);
+}
+
+inline RRDSET *rrdset_find_bytype(RRDHOST *host, const char *type, const char *id) {
+    debug(D_RRD_CALLS, "rrdset_find_bytype() for chart '%s.%s' in host '%s'", type, id, host->hostname);
+
+    char buf[RRD_ID_LENGTH_MAX + 1];
+    strncpyz(buf, type, RRD_ID_LENGTH_MAX - 1);
+    strcat(buf, ".");
+    int len = (int) strlen(buf);
+    strncpyz(&buf[len], id, (size_t) (RRD_ID_LENGTH_MAX - len));
+
+    return(rrdset_find(host, buf));
+}
+
+inline RRDSET *rrdset_find_byname(RRDHOST *host, const char *name) {
+    debug(D_RRD_CALLS, "rrdset_find_byname() for chart '%s' in host '%s'", name, host->hostname);
+    RRDSET *st = rrdset_index_find_name(host, name, 0);
+    return(st);
+}
+
+// ----------------------------------------------------------------------------
+// RRDSET - rename charts
+
+char *rrdset_strncpyz_name(char *to, const char *from, size_t length) {
+    char c, *p = to;
+
+    while (length-- && (c = *from++)) {
+        if(c != '.' && !isalnum(c))
+            c = '_';
+
+        *p++ = c;
+    }
+
+    *p = '\0';
+
+    return to;
+}
+
+void rrdset_set_name(RRDSET *st, const char *name) {
+    if(unlikely(st->name && !strcmp(st->name, name)))
+        return;
+
+    debug(D_RRD_CALLS, "rrdset_set_name() old: %s, new: %s", st->name, name);
+
+    char b[CONFIG_MAX_VALUE + 1];
+    char n[RRD_ID_LENGTH_MAX + 1];
+
+    snprintfz(n, RRD_ID_LENGTH_MAX, "%s.%s", st->type, name);
+    rrdset_strncpyz_name(b, n, CONFIG_MAX_VALUE);
+
+    if(st->name) {
+        rrdset_index_del_name(st->rrdhost, st);
+        st->name = config_set_default(st->config_section, "name", b);
+        st->hash_name = simple_hash(st->name);
+        rrdsetvar_rename_all(st);
+    }
+    else {
+        st->name = config_get(st->config_section, "name", b);
+        st->hash_name = simple_hash(st->name);
+    }
+
+    rrdset_wrlock(st);
+    RRDDIM *rd;
+    rrddim_foreach_write(rd, st)
+        rrddimvar_rename_all(rd);
+    rrdset_unlock(st);
+
+    if(unlikely(rrdset_index_add_name(st->rrdhost, st) != st))
+        error("RRDSET: INTERNAL ERROR: attempted to index duplicate chart name '%s'", st->name);
+}
+
+
+// ----------------------------------------------------------------------------
+// RRDSET - reset a chart
+
+void rrdset_reset(RRDSET *st) {
+    debug(D_RRD_CALLS, "rrdset_reset() %s", st->name);
+
+    st->last_collected_time.tv_sec = 0;
+    st->last_collected_time.tv_usec = 0;
+    st->last_updated.tv_sec = 0;
+    st->last_updated.tv_usec = 0;
+    st->current_entry = 0;
+    st->counter = 0;
+    st->counter_done = 0;
+
+    RRDDIM *rd;
+    rrddim_foreach_read(rd, st) {
+        rd->last_collected_time.tv_sec = 0;
+        rd->last_collected_time.tv_usec = 0;
+        rd->collections_counter = 0;
+        memset(rd->values, 0, rd->entries * sizeof(storage_number));
+    }
+}
+
+// ----------------------------------------------------------------------------
+// RRDSET - helpers for rrdset_create()
+
+inline long align_entries_to_pagesize(RRD_MEMORY_MODE mode, long entries) {
+    if(unlikely(entries < 5)) entries = 5;
+    if(unlikely(entries > RRD_HISTORY_ENTRIES_MAX)) entries = RRD_HISTORY_ENTRIES_MAX;
+
+    if(unlikely(mode == RRD_MEMORY_MODE_NONE || mode == RRD_MEMORY_MODE_RAM))
+        return entries;
+
+    long page = (size_t)sysconf(_SC_PAGESIZE);
+    long size = sizeof(RRDDIM) + entries * sizeof(storage_number);
+    if(unlikely(size % page)) {
+        size -= (size % page);
+        size += page;
+
+        long n = (size - sizeof(RRDDIM)) / sizeof(storage_number);
+        return n;
+    }
+
+    return entries;
+}
+
+static inline void last_collected_time_align(struct timeval *tv, int update_every) {
+    tv->tv_sec -= tv->tv_sec % update_every;
+    tv->tv_usec = 500000;
+}
+
+static inline void last_updated_time_align(struct timeval *tv, int update_every) {
+    tv->tv_sec -= tv->tv_sec % update_every;
+    tv->tv_usec = 0;
+}
+
+// ----------------------------------------------------------------------------
+// RRDSET - free a chart
+
+void rrdset_free(RRDSET *st) {
+    if(unlikely(!st)) return;
+
+    rrdhost_check_wrlock(st->rrdhost);  // make sure we have a write lock on the host
+    rrdset_wrlock(st);                  // lock this RRDSET
+
+    // ------------------------------------------------------------------------
+    // free its children structures
+
+    while(st->variables)  rrdsetvar_free(st->variables);
+    while(st->alarms)     rrdsetcalc_unlink(st->alarms);
+    while(st->dimensions) rrddim_free(st, st->dimensions);
+
+    rrdfamily_free(st->rrdhost, st->rrdfamily);
+
+    // ------------------------------------------------------------------------
+    // remove it from the indexes
+
+    if(unlikely(rrdset_index_del(st->rrdhost, st) != st))
+        error("RRDSET: INTERNAL ERROR: attempt to remove from index chart '%s', removed a different chart.", st->id);
+
+    rrdset_index_del_name(st->rrdhost, st);
+
+    // ------------------------------------------------------------------------
+    // unlink it from the host
+
+    if(st == st->rrdhost->rrdset_root) {
+        st->rrdhost->rrdset_root = st->next;
+    }
+    else {
+        // find the previous one
+        RRDSET *s;
+        for(s = st->rrdhost->rrdset_root; s && s->next != st ; s = s->next) ;
+
+        // bypass it
+        if(s) s->next = st->next;
+        else error("Request to free RRDSET '%s': cannot find it under host '%s'", st->id, st->rrdhost->hostname);
+    }
+
+    rrdset_unlock(st);
+
+    // ------------------------------------------------------------------------
+    // free it
+
+    // free directly allocated members
+    freez(st->config_section);
+
+    if(st->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || st->rrd_memory_mode == RRD_MEMORY_MODE_MAP) {
+        debug(D_RRD_CALLS, "Unmapping stats '%s'.", st->name);
+        munmap(st, st->memsize);
+    }
+    else
+        freez(st);
+}
+
+// ----------------------------------------------------------------------------
+// RRDSET - create a chart
+
+RRDSET *rrdset_create(RRDHOST *host, const char *type, const char *id, const char *name, const char *family
+                      , const char *context, const char *title, const char *units, long priority
+                      , int update_every, RRDSET_TYPE chart_type) {
+
+    if(!type || !type[0]) {
+        fatal("Cannot create rrd stats without a type.");
+        return NULL;
+    }
+
+    if(!id || !id[0]) {
+        fatal("Cannot create rrd stats without an id.");
+        return NULL;
+    }
+
+    // ------------------------------------------------------------------------
+    // check if it already exists
+
+    char fullid[RRD_ID_LENGTH_MAX + 1];
+    snprintfz(fullid, RRD_ID_LENGTH_MAX, "%s.%s", type, id);
+
+    RRDSET *st = rrdset_find(host, fullid);
+    if(st) {
+        debug(D_RRD_CALLS, "RRDSET '%s', already exists.", fullid);
+        return st;
+    }
+
+    char fullfilename[FILENAME_MAX + 1];
+
+    // ------------------------------------------------------------------------
+    // compose the config_section for this chart
+
+    char config_section[RRD_ID_LENGTH_MAX + 1];
+    if(host == localhost)
+        strcpy(config_section, fullid);
+    else
+        snprintfz(config_section, RRD_ID_LENGTH_MAX, "%s/%s", host->machine_guid, fullid);
+
+    // ------------------------------------------------------------------------
+    // get the options from the config, we need to create it
+
+    long rentries = config_get_number(config_section, "history", host->rrd_history_entries);
+    long entries = align_entries_to_pagesize(host->rrd_memory_mode, rentries);
+    if(entries != rentries) entries = config_set_number(config_section, "history", entries);
+
+    if(host->rrd_memory_mode == RRD_MEMORY_MODE_NONE && entries != rentries)
+        entries = config_set_number(config_section, "history", 10);
+
+    int enabled = config_get_boolean(config_section, "enabled", 1);
+    if(!enabled) entries = 5;
+
+    unsigned long size = sizeof(RRDSET);
+    char *cache_dir = rrdset_cache_dir(host, fullid, config_section);
+
+    time_t now = now_realtime_sec();
+
+    // ------------------------------------------------------------------------
+    // load it or allocate it
+
+    debug(D_RRD_CALLS, "Creating RRD_STATS for '%s.%s'.", type, id);
+
+    snprintfz(fullfilename, FILENAME_MAX, "%s/main.db", cache_dir);
+    if(host->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || host->rrd_memory_mode == RRD_MEMORY_MODE_MAP) {
+        st = (RRDSET *) mymmap(fullfilename, size, ((host->rrd_memory_mode == RRD_MEMORY_MODE_MAP) ? MAP_SHARED : MAP_PRIVATE), 0);
+        if(st) {
+            memset(&st->avl, 0, sizeof(avl));
+            memset(&st->avlname, 0, sizeof(avl));
+            memset(&st->variables_root_index, 0, sizeof(avl_tree_lock));
+            memset(&st->dimensions_index, 0, sizeof(avl_tree_lock));
+            memset(&st->rrdset_rwlock, 0, sizeof(pthread_rwlock_t));
+
+            st->name = NULL;
+            st->type = NULL;
+            st->family = NULL;
+            st->context = NULL;
+            st->title = NULL;
+            st->units = NULL;
+            st->dimensions = NULL;
+            st->next = NULL;
+            st->variables = NULL;
+            st->alarms = NULL;
+            st->flags = 0x00000000;
+
+            if(strcmp(st->magic, RRDSET_MAGIC) != 0) {
+                errno = 0;
+                info("Initializing file %s.", fullfilename);
+                memset(st, 0, size);
+            }
+            else if(strcmp(st->id, fullid) != 0) {
+                errno = 0;
+                error("File %s contents are not for chart %s. Clearing it.", fullfilename, fullid);
+                // munmap(st, size);
+                // st = NULL;
+                memset(st, 0, size);
+            }
+            else if(st->memsize != size || st->entries != entries) {
+                errno = 0;
+                error("File %s does not have the desired size. Clearing it.", fullfilename);
+                memset(st, 0, size);
+            }
+            else if(st->update_every != update_every) {
+                errno = 0;
+                error("File %s does not have the desired update frequency. Clearing it.", fullfilename);
+                memset(st, 0, size);
+            }
+            else if((now - st->last_updated.tv_sec) > update_every * entries) {
+                errno = 0;
+                error("File %s is too old. Clearing it.", fullfilename);
+                memset(st, 0, size);
+            }
+            else if(st->last_updated.tv_sec > now + update_every) {
+                errno = 0;
+                error("File %s refers to the future. Clearing it.", fullfilename);
+                memset(st, 0, size);
+            }
+
+            // make sure the database is aligned
+            if(st->last_updated.tv_sec)
+                last_updated_time_align(&st->last_updated, update_every);
+
+
+            // make sure we have the right memory mode
+            // even if we cleared the memory
+            st->rrd_memory_mode = host->rrd_memory_mode;
+        }
+    }
+
+    if(unlikely(!st)) {
+        st = callocz(1, size);
+        st->rrd_memory_mode = (host->rrd_memory_mode == RRD_MEMORY_MODE_NONE) ? RRD_MEMORY_MODE_NONE : RRD_MEMORY_MODE_RAM;
+    }
+
+    st->config_section = strdup(config_section);
+    st->rrdhost = host;
+    st->memsize = size;
+    st->entries = entries;
+    st->update_every = update_every;
+
+    if(st->current_entry >= st->entries) st->current_entry = 0;
+
+    strcpy(st->cache_filename, fullfilename);
+    strcpy(st->magic, RRDSET_MAGIC);
+
+    strcpy(st->id, fullid);
+    st->hash = simple_hash(st->id);
+
+    st->cache_dir = cache_dir;
+
+    st->chart_type = rrdset_type_id(config_get(st->config_section, "chart type", rrdset_type_name(chart_type)));
+    st->type       = config_get(st->config_section, "type", type);
+    st->family     = config_get(st->config_section, "family", family?family:st->type);
+    st->units      = config_get(st->config_section, "units", units?units:"");
+
+    st->context    = config_get(st->config_section, "context", context?context:st->id);
+    st->hash_context = simple_hash(st->context);
+
+    st->priority = config_get_number(st->config_section, "priority", priority);
+    if(enabled)
+        rrdset_flag_set(st, RRDSET_FLAG_ENABLED);
+    else
+        rrdset_flag_clear(st, RRDSET_FLAG_ENABLED);
+
+    rrdset_flag_clear(st, RRDSET_FLAG_DETAIL);
+    rrdset_flag_clear(st, RRDSET_FLAG_DEBUG);
+
+    // if(!strcmp(st->id, "disk_util.dm-0")) {
+    //     st->debug = 1;
+    //     error("enabled debugging for '%s'", st->id);
+    // }
+    // else error("not enabled debugging for '%s'", st->id);
+
+    st->green = NAN;
+    st->red = NAN;
+
+    st->last_collected_time.tv_sec = 0;
+    st->last_collected_time.tv_usec = 0;
+    st->counter_done = 0;
+
+    st->gap_when_lost_iterations_above = (int) (
+            config_get_number(st->config_section, "gap when lost iterations above", RRD_DEFAULT_GAP_INTERPOLATIONS) + 2);
+
+    avl_init_lock(&st->dimensions_index, rrddim_compare);
+    avl_init_lock(&st->variables_root_index, rrdvar_compare);
+
+    pthread_rwlock_init(&st->rrdset_rwlock, NULL);
+    rrdhost_wrlock(host);
+
+    if(name && *name) rrdset_set_name(st, name);
+    else rrdset_set_name(st, id);
+
+    {
+        char varvalue[CONFIG_MAX_VALUE + 1];
+        char varvalue2[CONFIG_MAX_VALUE + 1];
+        snprintfz(varvalue, CONFIG_MAX_VALUE, "%s (%s)", title?title:"", st->name);
+        json_escape_string(varvalue2, varvalue, sizeof(varvalue2));
+        st->title = config_get(st->config_section, "title", varvalue2);
+    }
+
+    st->rrdfamily = rrdfamily_create(host, st->family);
+
+    st->next = host->rrdset_root;
+    host->rrdset_root = st;
+
+    if(host->health_enabled) {
+        rrdsetvar_create(st, "last_collected_t", RRDVAR_TYPE_TIME_T, &st->last_collected_time.tv_sec, 0);
+        rrdsetvar_create(st, "collected_total_raw", RRDVAR_TYPE_TOTAL, &st->last_collected_total, 0);
+        rrdsetvar_create(st, "green", RRDVAR_TYPE_CALCULATED, &st->green, 0);
+        rrdsetvar_create(st, "red", RRDVAR_TYPE_CALCULATED, &st->red, 0);
+        rrdsetvar_create(st, "update_every", RRDVAR_TYPE_INT, &st->update_every, 0);
+    }
+
+    if(unlikely(rrdset_index_add(host, st) != st))
+        error("RRDSET: INTERNAL ERROR: attempt to index duplicate chart '%s'", st->id);
+
+    rrdsetcalc_link_matching(st);
+    rrdcalctemplate_link_matching(st);
+
+    rrdhost_unlock(host);
+
+    return(st);
+}
+
+
+// ----------------------------------------------------------------------------
+// RRDSET - data collection iteration control
+
+inline void rrdset_next_usec_unfiltered(RRDSET *st, usec_t microseconds) {
+
+    if(unlikely(!st->last_collected_time.tv_sec)) {
+        // the first entry
+        microseconds = st->update_every * USEC_PER_SEC;
+    }
+    else if(unlikely(!microseconds)) {
+        // no dt given by the plugin
+        struct timeval now;
+        now_realtime_timeval(&now);
+        microseconds = dt_usec(&now, &st->last_collected_time);
+    }
+
+    st->usec_since_last_update = microseconds;
+}
+
+inline void rrdset_next_usec(RRDSET *st, usec_t microseconds) {
+    struct timeval now;
+    now_realtime_timeval(&now);
+
+    if(unlikely(!st->last_collected_time.tv_sec)) {
+        // the first entry
+        microseconds = st->update_every * USEC_PER_SEC;
+    }
+    else if(unlikely(!microseconds)) {
+        // no dt given by the plugin
+        microseconds = dt_usec(&now, &st->last_collected_time);
+    }
+    else {
+        // microseconds has the time since the last collection
+//#ifdef NETDATA_INTERNAL_CHECKS
+//        usec_t now_usec = timeval_usec(&now);
+//        usec_t last_usec = timeval_usec(&st->last_collected_time);
+//#endif
+        susec_t since_last_usec = dt_usec_signed(&now, &st->last_collected_time);
+
+        if(unlikely(since_last_usec < 0)) {
+            // oops! the database is in the future
+            error("Database for chart '%s' on host '%s' is %lld microseconds in the future. Adjusting it to current time.", st->id, st->rrdhost->hostname, -since_last_usec);
+
+            st->last_collected_time.tv_sec  = now.tv_sec - st->update_every;
+            st->last_collected_time.tv_usec = now.tv_usec;
+            last_collected_time_align(&st->last_collected_time, st->update_every);
+
+            st->last_updated.tv_sec  = now.tv_sec - st->update_every;
+            st->last_updated.tv_usec = now.tv_usec;
+            last_updated_time_align(&st->last_updated, st->update_every);
+
+            microseconds    = st->update_every * USEC_PER_SEC;
+            since_last_usec = st->update_every * USEC_PER_SEC;
+        }
+
+        // verify the microseconds given is good
+        if(unlikely(microseconds > (usec_t)since_last_usec)) {
+            debug(D_RRD_CALLS, "dt %llu usec given is too big - it leads %llu usec to the future, for chart '%s' (%s).", microseconds, microseconds - (usec_t)since_last_usec, st->name, st->id);
+
+//#ifdef NETDATA_INTERNAL_CHECKS
+//            if(unlikely(last_usec + microseconds > now_usec + 1000))
+//                error("dt %llu usec given is too big - it leads %llu usec to the future, for chart '%s' (%s).", microseconds, microseconds - (usec_t)since_last_usec, st->name, st->id);
+//#endif
+
+            microseconds = (usec_t)since_last_usec;
+        }
+        else if(unlikely(microseconds < (usec_t)since_last_usec * 0.8)) {
+            debug(D_RRD_CALLS, "dt %llu usec given is too small - expected %llu usec up to -20%%, for chart '%s' (%s).", microseconds, (usec_t)since_last_usec, st->name, st->id);
+
+//#ifdef NETDATA_INTERNAL_CHECKS
+//            error("dt %llu usec given is too small - expected %llu usec up to -20%%, for chart '%s' (%s).", microseconds, (usec_t)since_last_usec, st->name, st->id);
+//#endif
+            microseconds = (usec_t)since_last_usec;
+        }
+    }
+    debug(D_RRD_CALLS, "rrdset_next_usec() for chart %s with microseconds %llu", st->name, microseconds);
+
+    if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+        debug(D_RRD_STATS, "%s: NEXT: %llu microseconds", st->name, microseconds);
+
+    st->usec_since_last_update = microseconds;
+}
+
+
+// ----------------------------------------------------------------------------
+// RRDSET - process the collected values for all dimensions of a chart
+
+static inline void rrdset_init_last_collected_time(RRDSET *st) {
+    now_realtime_timeval(&st->last_collected_time);
+    last_collected_time_align(&st->last_collected_time, st->update_every);
+}
+
+static inline usec_t rrdset_update_last_collected_time(RRDSET *st) {
+    usec_t last_collect_ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec;
+    usec_t ut = last_collect_ut + st->usec_since_last_update;
+    st->last_collected_time.tv_sec = (time_t) (ut / USEC_PER_SEC);
+    st->last_collected_time.tv_usec = (suseconds_t) (ut % USEC_PER_SEC);
+    return last_collect_ut;
+}
+
+static inline void rrdset_init_last_updated_time(RRDSET *st) {
+    // copy the last collected time to last updated time
+    st->last_updated.tv_sec  = st->last_collected_time.tv_sec;
+    st->last_updated.tv_usec = st->last_collected_time.tv_usec;
+    last_updated_time_align(&st->last_updated, st->update_every);
+}
+
+static inline void rrdset_done_push_exclusive(RRDSET *st) {
+    if(unlikely(!st->last_collected_time.tv_sec)) {
+        // it is the first entry
+        // set the last_collected_time to now
+        rrdset_init_last_collected_time(st);
+    }
+    else {
+        // it is not the first entry
+        // calculate the proper last_collected_time, using usec_since_last_update
+        rrdset_update_last_collected_time(st);
+    }
+
+    st->counter_done++;
+
+    rrdset_rdlock(st);
+    rrdset_done_push(st);
+    rrdset_unlock(st);
+}
+
+void rrdset_done(RRDSET *st) {
+    if(unlikely(netdata_exit)) return;
+
+    if(unlikely(st->rrd_memory_mode == RRD_MEMORY_MODE_NONE)) {
+        if(unlikely(st->rrdhost->rrdpush_enabled))
+            rrdset_done_push_exclusive(st);
+
+        return;
+    }
+
+    debug(D_RRD_CALLS, "rrdset_done() for chart %s", st->name);
+
+    RRDDIM *rd;
+
+    int
+            pthreadoldcancelstate;  // store the old cancelable pthread state, to restore it at the end
+
+    char
+            store_this_entry = 1,   // boolean: 1 = store this entry, 0 = don't store this entry
+            first_entry = 0;        // boolean: 1 = this is the first entry seen for this chart, 0 = all other entries
+
+    unsigned int
+            stored_entries = 0;     // the number of entries we have stored in the db, during this call to rrdset_done()
+
+    usec_t
+            last_collect_ut,        // the timestamp in microseconds, of the last collected value
+            now_collect_ut,         // the timestamp in microseconds, of this collected value (this is NOW)
+            last_stored_ut,         // the timestamp in microseconds, of the last stored entry in the db
+            next_store_ut,          // the timestamp in microseconds, of the next entry to store in the db
+            update_every_ut = st->update_every * USEC_PER_SEC; // st->update_every in microseconds
+
+    if(unlikely(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &pthreadoldcancelstate) != 0))
+        error("Cannot set pthread cancel state to DISABLE.");
+
+    // a read lock is OK here
+    rrdset_rdlock(st);
+
+/*
+    // enable the chart, if it was disabled
+    if(unlikely(rrd_delete_unupdated_dimensions) && !st->enabled)
+        st->enabled = 1;
+*/
+
+    // check if the chart has a long time to be updated
+    if(unlikely(st->usec_since_last_update > st->entries * update_every_ut)) {
+        info("%s: took too long to be updated (%0.3Lf secs). Resetting it.", st->name, (long double)(st->usec_since_last_update / 1000000.0));
+        rrdset_reset(st);
+        st->usec_since_last_update = update_every_ut;
+        first_entry = 1;
+    }
+
+    if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+        debug(D_RRD_STATS, "%s: microseconds since last update: %llu", st->name, st->usec_since_last_update);
+
+    // set last_collected_time
+    if(unlikely(!st->last_collected_time.tv_sec)) {
+        // it is the first entry
+        // set the last_collected_time to now
+        rrdset_init_last_collected_time(st);
+
+        last_collect_ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec - update_every_ut;
+
+        // the first entry should not be stored
+        store_this_entry = 0;
+        first_entry = 1;
+
+        if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+            debug(D_RRD_STATS, "%s: has not set last_collected_time. Setting it now. Will not store the next entry.", st->name);
+    }
+    else {
+        // it is not the first entry
+        // calculate the proper last_collected_time, using usec_since_last_update
+        last_collect_ut = rrdset_update_last_collected_time(st);
+    }
+
+    // if this set has not been updated in the past
+    // we fake the last_update time to be = now - usec_since_last_update
+    if(unlikely(!st->last_updated.tv_sec)) {
+        // it has never been updated before
+        // set a fake last_updated, in the past using usec_since_last_update
+        rrdset_init_last_updated_time(st);
+
+        // the first entry should not be stored
+        store_this_entry = 0;
+        first_entry = 1;
+
+        if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+            debug(D_RRD_STATS, "%s: initializing last_updated to last_collected_time - %llu microseconds. Will not store the next entry.", st->name, st->usec_since_last_update);
+    }
+
+    // check if we will re-write the entire data set
+    if(unlikely(dt_usec(&st->last_collected_time, &st->last_updated) > st->entries * update_every_ut)) {
+        info("%s: too old data (last updated at %ld.%ld, last collected at %ld.%ld). Resetting it. Will not store the next entry.", st->name, st->last_updated.tv_sec, st->last_updated.tv_usec, st->last_collected_time.tv_sec, st->last_collected_time.tv_usec);
+        rrdset_reset(st);
+        rrdset_init_last_updated_time(st);
+
+        st->usec_since_last_update = update_every_ut;
+
+        // the first entry should not be stored
+        store_this_entry = 0;
+        first_entry = 1;
+    }
+
+    // these are the 3 variables that will help us in interpolation
+    // last_stored_ut = the last time we added a value to the storage
+    // now_collect_ut = the time the current value has been collected
+    // next_store_ut  = the time of the next interpolation point
+    last_stored_ut = st->last_updated.tv_sec * USEC_PER_SEC + st->last_updated.tv_usec;
+    now_collect_ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec;
+    next_store_ut  = (st->last_updated.tv_sec + st->update_every) * USEC_PER_SEC;
+
+    if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))) {
+        debug(D_RRD_STATS, "%s: last_collect_ut = %0.3Lf (last collection time)", st->name, (long double)last_collect_ut/1000000.0);
+        debug(D_RRD_STATS, "%s: now_collect_ut  = %0.3Lf (current collection time)", st->name, (long double)now_collect_ut/1000000.0);
+        debug(D_RRD_STATS, "%s: last_stored_ut  = %0.3Lf (last updated time)", st->name, (long double)last_stored_ut/1000000.0);
+        debug(D_RRD_STATS, "%s: next_store_ut   = %0.3Lf (next interpolation point)", st->name, (long double)next_store_ut/1000000.0);
+    }
+
+    if(unlikely(!st->counter_done)) {
+        store_this_entry = 0;
+        if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+            debug(D_RRD_STATS, "%s: Will not store the next entry.", st->name);
+    }
+    st->counter_done++;
+
+    if(unlikely(st->rrdhost->rrdpush_enabled))
+        rrdset_done_push(st);
+
+    // calculate totals and count the dimensions
+    int dimensions = 0;
+    st->collected_total = 0;
+    rrddim_foreach_read(rd, st) {
+        dimensions++;
+        if(likely(rd->updated))
+            st->collected_total += rd->collected_value;
+    }
+
+    uint32_t storage_flags = SN_EXISTS;
+
+    // process all dimensions to calculate their values
+    // based on the collected figures only
+    // at this stage we do not interpolate anything
+    rrddim_foreach_read(rd, st) {
+
+        if(unlikely(!rd->updated)) {
+            rd->calculated_value = 0;
+            continue;
+        }
+
+        if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+            debug(D_RRD_STATS, "%s/%s: START "
+                    " last_collected_value = " COLLECTED_NUMBER_FORMAT
+                    " collected_value = " COLLECTED_NUMBER_FORMAT
+                    " last_calculated_value = " CALCULATED_NUMBER_FORMAT
+                    " calculated_value = " CALCULATED_NUMBER_FORMAT
+                                      , st->id, rd->name
+                                      , rd->last_collected_value
+                                      , rd->collected_value
+                                      , rd->last_calculated_value
+                                      , rd->calculated_value
+            );
+
+        switch(rd->algorithm) {
+            case RRD_ALGORITHM_ABSOLUTE:
+                rd->calculated_value = (calculated_number)rd->collected_value
+                                       * (calculated_number)rd->multiplier
+                                       / (calculated_number)rd->divisor;
+
+                if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+                    debug(D_RRD_STATS, "%s/%s: CALC ABS/ABS-NO-IN "
+                            CALCULATED_NUMBER_FORMAT " = "
+                            COLLECTED_NUMBER_FORMAT
+                            " * " CALCULATED_NUMBER_FORMAT
+                            " / " CALCULATED_NUMBER_FORMAT
+                          , st->id, rd->name
+                          , rd->calculated_value
+                          , rd->collected_value
+                          , (calculated_number)rd->multiplier
+                          , (calculated_number)rd->divisor
+                    );
+                break;
+
+            case RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL:
+                if(unlikely(!st->collected_total))
+                    rd->calculated_value = 0;
+                else
+                    // the percentage of the current value
+                    // over the total of all dimensions
+                    rd->calculated_value =
+                            (calculated_number)100
+                            * (calculated_number)rd->collected_value
+                            / (calculated_number)st->collected_total;
+
+                if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+                    debug(D_RRD_STATS, "%s/%s: CALC PCENT-ROW "
+                            CALCULATED_NUMBER_FORMAT " = 100"
+                                    " * " COLLECTED_NUMBER_FORMAT
+                            " / " COLLECTED_NUMBER_FORMAT
+                          , st->id, rd->name
+                          , rd->calculated_value
+                          , rd->collected_value
+                          , st->collected_total
+                    );
+                break;
+
+            case RRD_ALGORITHM_INCREMENTAL:
+                if(unlikely(rd->collections_counter <= 1)) {
+                    rd->calculated_value = 0;
+                    continue;
+                }
+
+                // if the new is smaller than the old (an overflow, or reset), set the old equal to the new
+                // to reset the calculation (it will give zero as the calculation for this second)
+                if(unlikely(rd->last_collected_value > rd->collected_value)) {
+                    debug(D_RRD_STATS, "%s.%s: RESET or OVERFLOW. Last collected value = " COLLECTED_NUMBER_FORMAT ", current = " COLLECTED_NUMBER_FORMAT
+                          , st->name, rd->name
+                          , rd->last_collected_value
+                          , rd->collected_value);
+
+                    if(!(rrddim_flag_check(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS)))
+                        storage_flags = SN_EXISTS_RESET;
+
+                    rd->last_collected_value = rd->collected_value;
+                }
+
+                rd->calculated_value +=
+                        (calculated_number)(rd->collected_value - rd->last_collected_value)
+                        * (calculated_number)rd->multiplier
+                        / (calculated_number)rd->divisor;
+
+                if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+                    debug(D_RRD_STATS, "%s/%s: CALC INC PRE "
+                            CALCULATED_NUMBER_FORMAT " = ("
+                            COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT
+                            ")"
+                                    " * " CALCULATED_NUMBER_FORMAT
+                            " / " CALCULATED_NUMBER_FORMAT
+                          , st->id, rd->name
+                          , rd->calculated_value
+                          , rd->collected_value, rd->last_collected_value
+                          , (calculated_number)rd->multiplier
+                          , (calculated_number)rd->divisor
+                    );
+                break;
+
+            case RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL:
+                if(unlikely(rd->collections_counter <= 1)) {
+                    rd->calculated_value = 0;
+                    continue;
+                }
+
+                // if the new is smaller than the old (an overflow, or reset), set the old equal to the new
+                // to reset the calculation (it will give zero as the calculation for this second)
+                if(unlikely(rd->last_collected_value > rd->collected_value)) {
+                    debug(D_RRD_STATS, "%s.%s: RESET or OVERFLOW. Last collected value = " COLLECTED_NUMBER_FORMAT ", current = " COLLECTED_NUMBER_FORMAT
+                          , st->name, rd->name
+                          , rd->last_collected_value
+                          , rd->collected_value);
+
+                    if(!(rrddim_flag_check(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS)))
+                        storage_flags = SN_EXISTS_RESET;
+
+                    rd->last_collected_value = rd->collected_value;
+                }
+
+                // the percentage of the current increment
+                // over the increment of all dimensions together
+                if(unlikely(st->collected_total == st->last_collected_total))
+                    rd->calculated_value = 0;
+                else
+                    rd->calculated_value =
+                            (calculated_number)100
+                            * (calculated_number)(rd->collected_value - rd->last_collected_value)
+                            / (calculated_number)(st->collected_total - st->last_collected_total);
+
+                if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+                    debug(D_RRD_STATS, "%s/%s: CALC PCENT-DIFF "
+                            CALCULATED_NUMBER_FORMAT " = 100"
+                                    " * (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")"
+                                    " / (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")"
+                          , st->id, rd->name
+                          , rd->calculated_value
+                          , rd->collected_value, rd->last_collected_value
+                          , st->collected_total, st->last_collected_total
+                    );
+                break;
+
+            default:
+                // make the default zero, to make sure
+                // it gets noticed when we add new types
+                rd->calculated_value = 0;
+
+                if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+                    debug(D_RRD_STATS, "%s/%s: CALC "
+                            CALCULATED_NUMBER_FORMAT " = 0"
+                          , st->id, rd->name
+                          , rd->calculated_value
+                    );
+                break;
+        }
+
+        if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+            debug(D_RRD_STATS, "%s/%s: PHASE2 "
+                    " last_collected_value = " COLLECTED_NUMBER_FORMAT
+                    " collected_value = " COLLECTED_NUMBER_FORMAT
+                    " last_calculated_value = " CALCULATED_NUMBER_FORMAT
+                    " calculated_value = " CALCULATED_NUMBER_FORMAT
+                                      , st->id, rd->name
+                                      , rd->last_collected_value
+                                      , rd->collected_value
+                                      , rd->last_calculated_value
+                                      , rd->calculated_value
+            );
+
+    }
+
+    // at this point we have all the calculated values ready
+    // it is now time to interpolate values on a second boundary
+
+    if(unlikely(now_collect_ut < next_store_ut)) {
+        // this is collected in the same interpolation point
+
+        if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+            debug(D_RRD_STATS, "%s: THIS IS IN THE SAME INTERPOLATION POINT", st->name);
+
+//#ifdef NETDATA_INTERNAL_CHECKS
+//        info("%s is collected in the same interpolation point: short by %llu microseconds", st->name, next_store_ut - now_collect_ut);
+//#endif
+    }
+
+    usec_t first_ut = last_stored_ut;
+    long long iterations = (now_collect_ut - last_stored_ut) / (update_every_ut);
+    if((now_collect_ut % (update_every_ut)) == 0) iterations++;
+
+    for( ; next_store_ut <= now_collect_ut ; last_collect_ut = next_store_ut, next_store_ut += update_every_ut, iterations-- ) {
+//#ifdef NETDATA_INTERNAL_CHECKS
+//        if(iterations < 0) { error("%s: iterations calculation wrapped! first_ut = %llu, last_stored_ut = %llu, next_store_ut = %llu, now_collect_ut = %llu", st->name, first_ut, last_stored_ut, next_store_ut, now_collect_ut); }
+//#endif
+
+        if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))) {
+            debug(D_RRD_STATS, "%s: last_stored_ut = %0.3Lf (last updated time)", st->name, (long double)last_stored_ut/1000000.0);
+            debug(D_RRD_STATS, "%s: next_store_ut  = %0.3Lf (next interpolation point)", st->name, (long double)next_store_ut/1000000.0);
+        }
+
+        st->last_updated.tv_sec = (time_t) (next_store_ut / USEC_PER_SEC);
+        st->last_updated.tv_usec = 0;
+
+        rrddim_foreach_read(rd, st) {
+            calculated_number new_value;
+
+            switch(rd->algorithm) {
+                case RRD_ALGORITHM_INCREMENTAL:
+                    new_value = (calculated_number)
+                            (      rd->calculated_value
+                                   * (calculated_number)(next_store_ut - last_collect_ut)
+                                   / (calculated_number)(now_collect_ut - last_collect_ut)
+                            );
+
+                    if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+                        debug(D_RRD_STATS, "%s/%s: CALC2 INC "
+                                CALCULATED_NUMBER_FORMAT " = "
+                                CALCULATED_NUMBER_FORMAT
+                                " * %llu"
+                                        " / %llu"
+                              , st->id, rd->name
+                              , new_value
+                              , rd->calculated_value
+                              , (next_store_ut - last_stored_ut)
+                              , (now_collect_ut - last_stored_ut)
+                        );
+
+                    rd->calculated_value -= new_value;
+                    new_value += rd->last_calculated_value;
+                    rd->last_calculated_value = 0;
+                    new_value /= (calculated_number)st->update_every;
+
+                    if(unlikely(next_store_ut - last_stored_ut < update_every_ut)) {
+                        if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+                            debug(D_RRD_STATS, "%s/%s: COLLECTION POINT IS SHORT " CALCULATED_NUMBER_FORMAT " - EXTRAPOLATING",
+                                    st->id, rd->name
+                                  , (calculated_number)(next_store_ut - last_stored_ut)
+                            );
+                        new_value = new_value * (calculated_number)(st->update_every * 1000000) / (calculated_number)(next_store_ut - last_stored_ut);
+                    }
+                    break;
+
+                case RRD_ALGORITHM_ABSOLUTE:
+                case RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL:
+                case RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL:
+                default:
+                    if(iterations == 1) {
+                        // this is the last iteration
+                        // do not interpolate
+                        // just show the calculated value
+
+                        new_value = rd->calculated_value;
+                    }
+                    else {
+                        // we have missed an update
+                        // interpolate in the middle values
+
+                        new_value = (calculated_number)
+                                (   (     (rd->calculated_value - rd->last_calculated_value)
+                                          * (calculated_number)(next_store_ut - last_collect_ut)
+                                          / (calculated_number)(now_collect_ut - last_collect_ut)
+                                    )
+                                    +  rd->last_calculated_value
+                                );
+
+                        if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+                            debug(D_RRD_STATS, "%s/%s: CALC2 DEF "
+                                    CALCULATED_NUMBER_FORMAT " = ((("
+                                            "(" CALCULATED_NUMBER_FORMAT " - " CALCULATED_NUMBER_FORMAT ")"
+                                            " * %llu"
+                                            " / %llu) + " CALCULATED_NUMBER_FORMAT
+                                  , st->id, rd->name
+                                  , new_value
+                                  , rd->calculated_value, rd->last_calculated_value
+                                  , (next_store_ut - first_ut)
+                                  , (now_collect_ut - first_ut), rd->last_calculated_value
+                            );
+                    }
+                    break;
+            }
+
+            if(unlikely(!store_this_entry)) {
+                rd->values[st->current_entry] = pack_storage_number(0, SN_NOT_EXISTS);
+                continue;
+            }
+
+            if(likely(rd->updated && rd->collections_counter > 1 && iterations < st->gap_when_lost_iterations_above)) {
+                rd->values[st->current_entry] = pack_storage_number(new_value, storage_flags );
+                rd->last_stored_value = new_value;
+
+                if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+                    debug(D_RRD_STATS, "%s/%s: STORE[%ld] "
+                            CALCULATED_NUMBER_FORMAT " = " CALCULATED_NUMBER_FORMAT
+                          , st->id, rd->name
+                          , st->current_entry
+                          , unpack_storage_number(rd->values[st->current_entry]), new_value
+                    );
+            }
+            else {
+                if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+                    debug(D_RRD_STATS, "%s/%s: STORE[%ld] = NON EXISTING "
+                          , st->id, rd->name
+                          , st->current_entry
+                    );
+                rd->values[st->current_entry] = pack_storage_number(0, SN_NOT_EXISTS);
+                rd->last_stored_value = NAN;
+            }
+
+            stored_entries++;
+
+            if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))) {
+                calculated_number t1 = new_value * (calculated_number)rd->multiplier / (calculated_number)rd->divisor;
+                calculated_number t2 = unpack_storage_number(rd->values[st->current_entry]);
+                calculated_number accuracy = accuracy_loss(t1, t2);
+                debug(D_RRD_STATS, "%s/%s: UNPACK[%ld] = " CALCULATED_NUMBER_FORMAT " FLAGS=0x%08x (original = " CALCULATED_NUMBER_FORMAT ", accuracy loss = " CALCULATED_NUMBER_FORMAT "%%%s)"
+                      , st->id, rd->name
+                      , st->current_entry
+                      , t2
+                      , get_storage_number_flags(rd->values[st->current_entry])
+                      , t1
+                      , accuracy
+                      , (accuracy > ACCURACY_LOSS) ? " **TOO BIG** " : ""
+                );
+
+                rd->collected_volume += t1;
+                rd->stored_volume += t2;
+                accuracy = accuracy_loss(rd->collected_volume, rd->stored_volume);
+                debug(D_RRD_STATS, "%s/%s: VOLUME[%ld] = " CALCULATED_NUMBER_FORMAT ", calculated  = " CALCULATED_NUMBER_FORMAT ", accuracy loss = " CALCULATED_NUMBER_FORMAT "%%%s"
+                      , st->id, rd->name
+                      , st->current_entry
+                      , rd->stored_volume
+                      , rd->collected_volume
+                      , accuracy
+                      , (accuracy > ACCURACY_LOSS) ? " **TOO BIG** " : ""
+                );
+
+            }
+        }
+        // reset the storage flags for the next point, if any;
+        storage_flags = SN_EXISTS;
+
+        st->counter++;
+        st->current_entry = ((st->current_entry + 1) >= st->entries) ? 0 : st->current_entry + 1;
+        last_stored_ut = next_store_ut;
+    }
+
+    st->last_collected_total  = st->collected_total;
+
+    rrddim_foreach_read(rd, st) {
+        if(unlikely(!rd->updated))
+            continue;
+
+        if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+            debug(D_RRD_STATS, "%s/%s: setting last_collected_value (old: " COLLECTED_NUMBER_FORMAT ") to last_collected_value (new: " COLLECTED_NUMBER_FORMAT ")", st->id, rd->name, rd->last_collected_value, rd->collected_value);
+
+        rd->last_collected_value = rd->collected_value;
+
+        switch(rd->algorithm) {
+            case RRD_ALGORITHM_INCREMENTAL:
+                if(unlikely(!first_entry)) {
+                    if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+                        debug(D_RRD_STATS, "%s/%s: setting last_calculated_value (old: " CALCULATED_NUMBER_FORMAT ") to last_calculated_value (new: " CALCULATED_NUMBER_FORMAT ")", st->id, rd->name, rd->last_calculated_value + rd->calculated_value, rd->calculated_value);
+                    rd->last_calculated_value += rd->calculated_value;
+                }
+                else {
+                    if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+                        debug(D_RRD_STATS, "%s: THIS IS THE FIRST POINT", st->name);
+                }
+                break;
+
+            case RRD_ALGORITHM_ABSOLUTE:
+            case RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL:
+            case RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL:
+                if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+                    debug(D_RRD_STATS, "%s/%s: setting last_calculated_value (old: " CALCULATED_NUMBER_FORMAT ") to last_calculated_value (new: " CALCULATED_NUMBER_FORMAT ")", st->id, rd->name, rd->last_calculated_value, rd->calculated_value);
+                rd->last_calculated_value = rd->calculated_value;
+                break;
+        }
+
+        rd->calculated_value = 0;
+        rd->collected_value = 0;
+        rd->updated = 0;
+
+        if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+            debug(D_RRD_STATS, "%s/%s: END "
+                    " last_collected_value = " COLLECTED_NUMBER_FORMAT
+                    " collected_value = " COLLECTED_NUMBER_FORMAT
+                    " last_calculated_value = " CALCULATED_NUMBER_FORMAT
+                    " calculated_value = " CALCULATED_NUMBER_FORMAT
+                                      , st->id, rd->name
+                                      , rd->last_collected_value
+                                      , rd->collected_value
+                                      , rd->last_calculated_value
+                                      , rd->calculated_value
+            );
+    }
+
+    // ALL DONE ABOUT THE DATA UPDATE
+    // --------------------------------------------------------------------
+
+/*
+    // find if there are any obsolete dimensions (not updated recently)
+    if(unlikely(rrd_delete_unupdated_dimensions)) {
+
+        for( rd = st->dimensions; likely(rd) ; rd = rd->next )
+            if((rd->last_collected_time.tv_sec + (rrd_delete_unupdated_dimensions * st->update_every)) < st->last_collected_time.tv_sec)
+                break;
+
+        if(unlikely(rd)) {
+            RRDDIM *last;
+            // there is dimension to free
+            // upgrade our read lock to a write lock
+            pthread_rwlock_unlock(&st->rrdset_rwlock);
+            pthread_rwlock_wrlock(&st->rrdset_rwlock);
+
+            for( rd = st->dimensions, last = NULL ; likely(rd) ; ) {
+                // remove it only it is not updated in rrd_delete_unupdated_dimensions seconds
+
+                if(unlikely((rd->last_collected_time.tv_sec + (rrd_delete_unupdated_dimensions * st->update_every)) < st->last_collected_time.tv_sec)) {
+                    info("Removing obsolete dimension '%s' (%s) of '%s' (%s).", rd->name, rd->id, st->name, st->id);
+
+                    if(unlikely(!last)) {
+                        st->dimensions = rd->next;
+                        rd->next = NULL;
+                        rrddim_free(st, rd);
+                        rd = st->dimensions;
+                        continue;
+                    }
+                    else {
+                        last->next = rd->next;
+                        rd->next = NULL;
+                        rrddim_free(st, rd);
+                        rd = last->next;
+                        continue;
+                    }
+                }
+
+                last = rd;
+                rd = rd->next;
+            }
+
+            if(unlikely(!st->dimensions)) {
+                info("Disabling chart %s (%s) since it does not have any dimensions", st->name, st->id);
+                st->enabled = 0;
+            }
+        }
+    }
+*/
+
+    rrdset_unlock(st);
+
+    if(unlikely(pthread_setcancelstate(pthreadoldcancelstate, NULL) != 0))
+        error("Cannot set pthread cancel state to RESTORE (%d).", pthreadoldcancelstate);
+}
diff --git a/src/rrdsetvar.c b/src/rrdsetvar.c
new file mode 100644 (file)
index 0000000..03d7aec
--- /dev/null
@@ -0,0 +1,120 @@
+#define NETDATA_HEALTH_INTERNALS
+#include "common.h"
+
+// ----------------------------------------------------------------------------
+// RRDSETVAR management
+// CHART VARIABLES
+
+static inline void rrdsetvar_free_variables(RRDSETVAR *rs) {
+    RRDSET *st = rs->rrdset;
+
+    // CHART
+
+    rrdvar_free(st->rrdhost, &st->variables_root_index, rs->var_local);
+    rs->var_local = NULL;
+
+    // FAMILY
+
+    rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family);
+    rs->var_family = NULL;
+
+    rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host);
+    rs->var_host = NULL;
+
+    // HOST
+
+    rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_name);
+    rs->var_family_name = NULL;
+
+    rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_name);
+    rs->var_host_name = NULL;
+
+    // KEYS
+
+    freez(rs->key_fullid);
+    rs->key_fullid = NULL;
+
+    freez(rs->key_fullname);
+    rs->key_fullname = NULL;
+}
+
+static inline void rrdsetvar_create_variables(RRDSETVAR *rs) {
+    rrdsetvar_free_variables(rs);
+
+    RRDSET *st = rs->rrdset;
+
+    // KEYS
+
+    char buffer[RRDVAR_MAX_LENGTH + 1];
+    snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->id, rs->variable);
+    rs->key_fullid = strdupz(buffer);
+
+    snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->name, rs->variable);
+    rs->key_fullname = strdupz(buffer);
+
+    // CHART
+
+    rs->var_local       = rrdvar_create_and_index("local",  &st->variables_root_index,               rs->variable, rs->type, rs->value);
+
+    // FAMILY
+
+    rs->var_family      = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index,    rs->key_fullid,   rs->type, rs->value);
+    rs->var_family_name = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index,    rs->key_fullname, rs->type, rs->value);
+
+    // HOST
+
+    rs->var_host        = rrdvar_create_and_index("host",   &st->rrdhost->variables_root_index,      rs->key_fullid,   rs->type, rs->value);
+    rs->var_host_name   = rrdvar_create_and_index("host",   &st->rrdhost->variables_root_index,      rs->key_fullname, rs->type, rs->value);
+
+}
+
+RRDSETVAR *rrdsetvar_create(RRDSET *st, const char *variable, int type, void *value, uint32_t options) {
+    debug(D_VARIABLES, "RRDVARSET create for chart id '%s' name '%s' with variable name '%s'", st->id, st->name, variable);
+    RRDSETVAR *rs = (RRDSETVAR *)callocz(1, sizeof(RRDSETVAR));
+
+    rs->variable = strdupz(variable);
+    rs->type = type;
+    rs->value = value;
+    rs->options = options;
+    rs->rrdset = st;
+
+    rs->next = st->variables;
+    st->variables = rs;
+
+    rrdsetvar_create_variables(rs);
+
+    return rs;
+}
+
+void rrdsetvar_rename_all(RRDSET *st) {
+    debug(D_VARIABLES, "RRDSETVAR rename for chart id '%s' name '%s'", st->id, st->name);
+
+    RRDSETVAR *rs, *next = st->variables;
+    while((rs = next)) {
+        next = rs->next;
+        rrdsetvar_create_variables(rs);
+    }
+
+    rrdsetcalc_link_matching(st);
+}
+
+void rrdsetvar_free(RRDSETVAR *rs) {
+    RRDSET *st = rs->rrdset;
+    debug(D_VARIABLES, "RRDSETVAR free for chart id '%s' name '%s', variable '%s'", st->id, st->name, rs->variable);
+
+    if(st->variables == rs) {
+        st->variables = rs->next;
+    }
+    else {
+        RRDSETVAR *t;
+        for (t = st->variables; t && t->next != rs; t = t->next);
+        if(!t) error("RRDSETVAR '%s' not found in chart '%s' variables linked list", rs->key_fullname, st->id);
+        else t->next = rs->next;
+    }
+
+    rrdsetvar_free_variables(rs);
+
+    freez(rs->variable);
+    freez(rs);
+}
+
diff --git a/src/rrdvar.c b/src/rrdvar.c
new file mode 100644 (file)
index 0000000..2223d7c
--- /dev/null
@@ -0,0 +1,265 @@
+#define NETDATA_HEALTH_INTERNALS
+#include "common.h"
+
+// ----------------------------------------------------------------------------
+// RRDVAR management
+
+inline int rrdvar_fix_name(char *variable) {
+    int fixed = 0;
+    while(*variable) {
+        if (!isalnum(*variable) && *variable != '.' && *variable != '_') {
+            *variable++ = '_';
+            fixed++;
+        }
+        else
+            variable++;
+    }
+
+    return fixed;
+}
+
+int rrdvar_compare(void* a, void* b) {
+    if(((RRDVAR *)a)->hash < ((RRDVAR *)b)->hash) return -1;
+    else if(((RRDVAR *)a)->hash > ((RRDVAR *)b)->hash) return 1;
+    else return strcmp(((RRDVAR *)a)->name, ((RRDVAR *)b)->name);
+}
+
+static inline RRDVAR *rrdvar_index_add(avl_tree_lock *tree, RRDVAR *rv) {
+    RRDVAR *ret = (RRDVAR *)avl_insert_lock(tree, (avl *)(rv));
+    if(ret != rv)
+        debug(D_VARIABLES, "Request to insert RRDVAR '%s' into index failed. Already exists.", rv->name);
+
+    return ret;
+}
+
+static inline RRDVAR *rrdvar_index_del(avl_tree_lock *tree, RRDVAR *rv) {
+    RRDVAR *ret = (RRDVAR *)avl_remove_lock(tree, (avl *)(rv));
+    if(!ret)
+        error("Request to remove RRDVAR '%s' from index failed. Not Found.", rv->name);
+
+    return ret;
+}
+
+static inline RRDVAR *rrdvar_index_find(avl_tree_lock *tree, const char *name, uint32_t hash) {
+    RRDVAR tmp;
+    tmp.name = (char *)name;
+    tmp.hash = (hash)?hash:simple_hash(tmp.name);
+
+    return (RRDVAR *)avl_search_lock(tree, (avl *)&tmp);
+}
+
+inline void rrdvar_free(RRDHOST *host, avl_tree_lock *tree, RRDVAR *rv) {
+    (void)host;
+
+    if(!rv) return;
+
+    if(tree) {
+        debug(D_VARIABLES, "Deleting variable '%s'", rv->name);
+        if(unlikely(!rrdvar_index_del(tree, rv)))
+            error("Attempted to delete variable '%s' from host '%s', but it is not found.", rv->name, host->hostname);
+    }
+
+    freez(rv->name);
+    freez(rv);
+}
+
+inline RRDVAR *rrdvar_create_and_index(const char *scope, avl_tree_lock *tree, const char *name, int type, void *value) {
+    char *variable = strdupz(name);
+    rrdvar_fix_name(variable);
+    uint32_t hash = simple_hash(variable);
+
+    RRDVAR *rv = rrdvar_index_find(tree, variable, hash);
+    if(unlikely(!rv)) {
+        debug(D_VARIABLES, "Variable '%s' not found in scope '%s'. Creating a new one.", variable, scope);
+
+        rv = callocz(1, sizeof(RRDVAR));
+        rv->name = variable;
+        rv->hash = hash;
+        rv->type = type;
+        rv->value = value;
+
+        RRDVAR *ret = rrdvar_index_add(tree, rv);
+        if(unlikely(ret != rv)) {
+            debug(D_VARIABLES, "Variable '%s' in scope '%s' already exists", variable, scope);
+            rrdvar_free(NULL, NULL, rv);
+            rv = NULL;
+        }
+        else
+            debug(D_VARIABLES, "Variable '%s' created in scope '%s'", variable, scope);
+    }
+    else {
+        debug(D_VARIABLES, "Variable '%s' is already found in scope '%s'.", variable, scope);
+
+        // already exists
+        freez(variable);
+
+        // this is important
+        // it must return NULL - not the existing variable - or double-free will happen
+        rv = NULL;
+    }
+
+    return rv;
+}
+
+// ----------------------------------------------------------------------------
+// CUSTOM VARIABLES
+
+RRDVAR *rrdvar_custom_host_variable_create(RRDHOST *host, const char *name) {
+    calculated_number *v = callocz(1, sizeof(calculated_number));
+    *v = NAN;
+    RRDVAR *rv = rrdvar_create_and_index("host", &host->variables_root_index, name, RRDVAR_TYPE_CALCULATED_ALLOCATED, v);
+    if(unlikely(!rv)) {
+        free(v);
+        error("Requested variable '%s' already exists - possibly 2 plugins will be updating it at the same time", name);
+
+        char *variable = strdupz(name);
+        rrdvar_fix_name(variable);
+        uint32_t hash = simple_hash(variable);
+
+        rv = rrdvar_index_find(&host->variables_root_index, variable, hash);
+    }
+
+    return rv;
+}
+
+void rrdvar_custom_host_variable_destroy(RRDHOST *host, const char *name) {
+    char *variable = strdupz(name);
+    rrdvar_fix_name(variable);
+    uint32_t hash = simple_hash(variable);
+
+    RRDVAR *rv = rrdvar_index_find(&host->variables_root_index, variable, hash);
+    freez(variable);
+
+    if(!rv) {
+        error("Attempted to remove variable '%s' from host '%s', but it does not exist.", name, host->hostname);
+        return;
+    }
+
+    if(rv->type != RRDVAR_TYPE_CALCULATED_ALLOCATED) {
+        error("Attempted to remove variable '%s' from host '%s', but it does not a custom allocated variable.", name, host->hostname);
+        return;
+    }
+
+    if(!rrdvar_index_del(&host->variables_root_index, rv)) {
+        error("Attempted to remove variable '%s' from host '%s', but it cannot be found.", name, host->hostname);
+        return;
+    }
+
+    freez(rv->name);
+    freez(rv->value);
+    freez(rv);
+}
+
+void rrdvar_custom_host_variable_set(RRDVAR *rv, calculated_number value) {
+    if(rv->type != RRDVAR_TYPE_CALCULATED_ALLOCATED)
+        error("requested to set variable '%s' to value " CALCULATED_NUMBER_FORMAT " but the variable is not a custom one.", rv->name, value);
+    else {
+        calculated_number *v = rv->value;
+        *v = value;
+    }
+}
+
+// ----------------------------------------------------------------------------
+// RRDVAR lookup
+
+static calculated_number rrdvar2number(RRDVAR *rv) {
+    switch(rv->type) {
+        case RRDVAR_TYPE_CALCULATED_ALLOCATED:
+        case RRDVAR_TYPE_CALCULATED: {
+            calculated_number *n = (calculated_number *)rv->value;
+            return *n;
+        }
+
+        case RRDVAR_TYPE_TIME_T: {
+            time_t *n = (time_t *)rv->value;
+            return *n;
+        }
+
+        case RRDVAR_TYPE_COLLECTED: {
+            collected_number *n = (collected_number *)rv->value;
+            return *n;
+        }
+
+        case RRDVAR_TYPE_TOTAL: {
+            total_number *n = (total_number *)rv->value;
+            return *n;
+        }
+
+        case RRDVAR_TYPE_INT: {
+            int *n = (int *)rv->value;
+            return *n;
+        }
+
+        default:
+            error("I don't know how to convert RRDVAR type %d to calculated_number", rv->type);
+            return NAN;
+    }
+}
+
+int health_variable_lookup(const char *variable, uint32_t hash, RRDCALC *rc, calculated_number *result) {
+    RRDSET *st = rc->rrdset;
+    RRDVAR *rv;
+
+    if(!st) return 0;
+
+    rv = rrdvar_index_find(&st->variables_root_index, variable, hash);
+    if(rv) {
+        *result = rrdvar2number(rv);
+        return 1;
+    }
+
+    rv = rrdvar_index_find(&st->rrdfamily->variables_root_index, variable, hash);
+    if(rv) {
+        *result = rrdvar2number(rv);
+        return 1;
+    }
+
+    rv = rrdvar_index_find(&st->rrdhost->variables_root_index, variable, hash);
+    if(rv) {
+        *result = rrdvar2number(rv);
+        return 1;
+    }
+
+    return 0;
+}
+
+// ----------------------------------------------------------------------------
+// RRDVAR to JSON
+
+struct variable2json_helper {
+    BUFFER *buf;
+    size_t counter;
+};
+
+static int single_variable2json(void *entry, void *data) {
+    struct variable2json_helper *helper = (struct variable2json_helper *)data;
+    RRDVAR *rv = (RRDVAR *)entry;
+    calculated_number value = rrdvar2number(rv);
+
+    if(unlikely(isnan(value) || isinf(value)))
+        buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": null", helper->counter?",":"", rv->name);
+    else
+        buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": %0.5Lf", helper->counter?",":"", rv->name, (long double)value);
+
+    helper->counter++;
+
+    return 0;
+}
+
+void health_api_v1_chart_variables2json(RRDSET *st, BUFFER *buf) {
+    struct variable2json_helper helper = {
+            .buf = buf,
+            .counter = 0
+    };
+
+    buffer_sprintf(buf, "{\n\t\"chart\": \"%s\",\n\t\"chart_name\": \"%s\",\n\t\"chart_context\": \"%s\",\n\t\"chart_variables\": {", st->id, st->name, st->context);
+    avl_traverse_lock(&st->variables_root_index, single_variable2json, (void *)&helper);
+    buffer_sprintf(buf, "\n\t},\n\t\"family\": \"%s\",\n\t\"family_variables\": {", st->family);
+    helper.counter = 0;
+    avl_traverse_lock(&st->rrdfamily->variables_root_index, single_variable2json, (void *)&helper);
+    buffer_sprintf(buf, "\n\t},\n\t\"host\": \"%s\",\n\t\"host_variables\": {", st->rrdhost->hostname);
+    helper.counter = 0;
+    avl_traverse_lock(&st->rrdhost->variables_root_index, single_variable2json, (void *)&helper);
+    buffer_strcat(buf, "\n\t}\n}\n");
+}
+
index 7e4424297f86c2c311f58a57dedaa6221dddf144..f72a42d06e6e53c01eb3e5d4cd76637d728bd902 100644 (file)
@@ -169,7 +169,7 @@ static inline int match_pattern(struct simple_pattern *m, const char *str, size_
 int simple_pattern_matches(SIMPLE_PATTERN *list, const char *str) {
     struct simple_pattern *m, *root = (struct simple_pattern *)list;
 
-    if(unlikely(!root)) return 0;
+    if(unlikely(!root || !str || !*str)) return 0;
 
     size_t len = strlen(str);
     for(m = root; m ; m = m->next)
@@ -184,8 +184,8 @@ int simple_pattern_matches(SIMPLE_PATTERN *list, const char *str) {
 static inline void free_pattern(struct simple_pattern *m) {
     if(!m) return;
 
-    if(m->next) free_pattern(m->next);
-    if(m->child) free_pattern(m->child);
+    free_pattern(m->child);
+    free_pattern(m->next);
     freez((void *)m->match);
     freez(m);
 }
@@ -193,5 +193,5 @@ static inline void free_pattern(struct simple_pattern *m) {
 void simple_pattern_free(SIMPLE_PATTERN *list) {
     if(!list) return;
 
-    free_pattern(((struct simple_pattern *)list)->next);
+    free_pattern(((struct simple_pattern *)list));
 }
index 643811e446786a834fdb7ba49f0762433ff5fbd9..400c1ef4e2456020c5fb36794dc7c8cc449522ab 100644 (file)
@@ -160,8 +160,10 @@ int connect_to(const char *definition, int default_port, struct timeval *timeout
 
         fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
         if(fd != -1) {
-            if(setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *)timeout, sizeof(struct timeval)) < 0)
-                error("Failed to set timeout on the socket to ip '%s' port '%s'", hostBfr, servBfr);
+            if(timeout) {
+                if(setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *) timeout, sizeof(struct timeval)) < 0)
+                    error("Failed to set timeout on the socket to ip '%s' port '%s'", hostBfr, servBfr);
+            }
 
             if(connect(fd, ai->ai_addr, ai->ai_addrlen) < 0) {
                 error("Failed to connect to '%s', port '%s'", hostBfr, servBfr);
@@ -177,3 +179,98 @@ int connect_to(const char *definition, int default_port, struct timeval *timeout
 
     return fd;
 }
+
+int connect_to_one_of(const char *destination, int default_port, struct timeval *timeout, size_t *reconnects_counter, char *connected_to, size_t connected_to_size) {
+    int sock = -1;
+
+    const char *s = destination;
+    while(*s) {
+        const char *e = s;
+
+        // skip separators, moving both s(tart) and e(nd)
+        while(isspace(*e) || *e == ',') s = ++e;
+
+        // move e(nd) to the first separator
+        while(*e && !isspace(*e) && *e != ',') e++;
+
+        // is there anything?
+        if(!*s || s == e) break;
+
+        char buf[e - s + 1];
+        strncpyz(buf, s, e - s);
+        if(reconnects_counter) *reconnects_counter += 1;
+        sock = connect_to(buf, default_port, timeout);
+        if(sock != -1) {
+            if(connected_to && connected_to_size) {
+                strncpy(connected_to, buf, connected_to_size);
+                connected_to[connected_to_size - 1] = '\0';
+            }
+            break;
+        }
+        s = e;
+    }
+
+    return sock;
+}
+
+ssize_t recv_timeout(int sockfd, void *buf, size_t len, int flags, int timeout) {
+    for(;;) {
+        struct pollfd fd = {
+                .fd = sockfd,
+                .events = POLLIN,
+                .revents = 0
+        };
+
+        errno = 0;
+        int retval = poll(&fd, 1, timeout * 1000);
+
+        if(retval == -1) {
+            // failed
+
+            if(errno == EINTR || errno == EAGAIN)
+                continue;
+
+            return -1;
+        }
+
+        if(!retval) {
+            // timeout
+            return 0;
+        }
+
+        if(fd.events & POLLIN) break;
+    }
+
+    return recv(sockfd, buf, len, flags);
+}
+
+ssize_t send_timeout(int sockfd, void *buf, size_t len, int flags, int timeout) {
+    for(;;) {
+        struct pollfd fd = {
+                .fd = sockfd,
+                .events = POLLOUT,
+                .revents = 0
+        };
+
+        errno = 0;
+        int retval = poll(&fd, 1, timeout * 1000);
+
+        if(retval == -1) {
+            // failed
+
+            if(errno == EINTR || errno == EAGAIN)
+                continue;
+
+            return -1;
+        }
+
+        if(!retval) {
+            // timeout
+            return 0;
+        }
+
+        if(fd.events & POLLOUT) break;
+    }
+
+    return send(sockfd, buf, len, flags);
+}
index 791c0ce547519e491881f8fa9024cc241cc0b1f5..89c154a6186147d66072cf55c81838e842edcab3 100644 (file)
@@ -6,5 +6,9 @@
 #define NETDATA_SOCKET_H
 
 extern int connect_to(const char *definition, int default_port, struct timeval *timeout);
+extern int connect_to_one_of(const char *destination, int default_port, struct timeval *timeout, size_t *reconnects_counter, char *connected_to, size_t connected_to_size);
+
+extern ssize_t recv_timeout(int sockfd, void *buf, size_t len, int flags, int timeout);
+extern ssize_t send_timeout(int sockfd, void *buf, size_t len, int flags, int timeout);
 
 #endif //NETDATA_SOCKET_H
index 260f5121fb727160a9b4b3f722764b0b35566993..c41ad7faa1d9401926343fa9132ede7fa0c4f831 100644 (file)
@@ -76,11 +76,11 @@ int do_proc_sys_devices_system_edac_mc(int update_every, usec_t dt) {
     struct mc *m;
 
     if(unlikely(do_ce == -1)) {
-        do_ce = config_get_boolean_ondemand("plugin:proc:/sys/devices/system/edac/mc", "enable ECC memory correctable errors", CONFIG_ONDEMAND_ONDEMAND);
-        do_ue = config_get_boolean_ondemand("plugin:proc:/sys/devices/system/edac/mc", "enable ECC memory uncorrectable errors", CONFIG_ONDEMAND_ONDEMAND);
+        do_ce = config_get_boolean_ondemand("plugin:proc:/sys/devices/system/edac/mc", "enable ECC memory correctable errors", CONFIG_BOOLEAN_AUTO);
+        do_ue = config_get_boolean_ondemand("plugin:proc:/sys/devices/system/edac/mc", "enable ECC memory uncorrectable errors", CONFIG_BOOLEAN_AUTO);
     }
 
-    if(do_ce != CONFIG_ONDEMAND_NO) {
+    if(do_ce != CONFIG_BOOLEAN_NO) {
         for(m = mc_root; m; m = m->next) {
             if(m->ce_count_filename) {
                 m->ce_updated = 0;
@@ -102,7 +102,7 @@ int do_proc_sys_devices_system_edac_mc(int update_every, usec_t dt) {
         }
     }
 
-    if(do_ue != CONFIG_ONDEMAND_NO) {
+    if(do_ue != CONFIG_BOOLEAN_NO) {
         for(m = mc_root; m; m = m->next) {
             if(m->ue_count_filename) {
                 m->ue_updated = 0;
@@ -126,20 +126,20 @@ int do_proc_sys_devices_system_edac_mc(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_ce == CONFIG_ONDEMAND_YES || (do_ce == CONFIG_ONDEMAND_ONDEMAND && ce_sum > 0)) {
-        do_ce = CONFIG_ONDEMAND_YES;
+    if(do_ce == CONFIG_BOOLEAN_YES || (do_ce == CONFIG_BOOLEAN_AUTO && ce_sum > 0)) {
+        do_ce = CONFIG_BOOLEAN_YES;
 
         static RRDSET *ce_st = NULL;
 
         if(unlikely(!ce_st)) {
-            ce_st = rrdset_find("mem.ecc_ce");
+            ce_st = rrdset_find_localhost("mem.ecc_ce");
             if(unlikely(!ce_st))
-                ce_st = rrdset_create("mem", "ecc_ce", NULL, "ecc", NULL, "ECC Memory Correctable Errors", "errors",
-                        6600, update_every, RRDSET_TYPE_LINE);
+                ce_st = rrdset_create_localhost("mem", "ecc_ce", NULL, "ecc", NULL, "ECC Memory Correctable Errors"
+                                                , "errors", 6600, update_every, RRDSET_TYPE_LINE);
 
             for(m = mc_root; m; m = m->next)
                 if(m->ce_count_filename)
-                    m->ce_rd = rrddim_add(ce_st, m->name, NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    m->ce_rd = rrddim_add(ce_st, m->name, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else
             rrdset_next(ce_st);
@@ -153,21 +153,21 @@ int do_proc_sys_devices_system_edac_mc(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    if(do_ue == CONFIG_ONDEMAND_YES || (do_ue == CONFIG_ONDEMAND_ONDEMAND && ue_sum > 0)) {
-        do_ue = CONFIG_ONDEMAND_YES;
+    if(do_ue == CONFIG_BOOLEAN_YES || (do_ue == CONFIG_BOOLEAN_AUTO && ue_sum > 0)) {
+        do_ue = CONFIG_BOOLEAN_YES;
 
         static RRDSET *ue_st = NULL;
 
         if(unlikely(!ue_st)) {
-            ue_st = rrdset_find("mem.ecc_ue");
+            ue_st = rrdset_find_localhost("mem.ecc_ue");
 
             if(unlikely(!ue_st))
-                ue_st = rrdset_create("mem", "ecc_ue", NULL, "ecc", NULL, "ECC Memory Uncorrectable Errors", "errors",
-                        6610, update_every, RRDSET_TYPE_LINE);
+                ue_st = rrdset_create_localhost("mem", "ecc_ue", NULL, "ecc", NULL, "ECC Memory Uncorrectable Errors"
+                                                , "errors", 6610, update_every, RRDSET_TYPE_LINE);
 
             for(m = mc_root; m; m = m->next)
                 if(m->ue_count_filename)
-                    m->ue_rd = rrddim_add(ue_st, m->name, NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    m->ue_rd = rrddim_add(ue_st, m->name, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
         }
         else
             rrdset_next(ue_st);
index eb0318295de9b97ffa852a85758a68ffb46baa0d..449bbf0ded7e39ae8bf30baee0717a4ade000fa1 100644 (file)
@@ -72,10 +72,10 @@ int do_proc_sys_devices_system_node(int update_every, usec_t dt) {
     struct node *m;
 
     if(unlikely(do_numastat == -1)) {
-        do_numastat = config_get_boolean_ondemand("plugin:proc:/sys/devices/system/node", "enable per-node numa metrics", CONFIG_ONDEMAND_ONDEMAND);
+        do_numastat = config_get_boolean_ondemand("plugin:proc:/sys/devices/system/node", "enable per-node numa metrics", CONFIG_BOOLEAN_AUTO);
     }
 
-    if(do_numastat == CONFIG_ONDEMAND_YES || (do_numastat == CONFIG_ONDEMAND_ONDEMAND && numa_node_count >= 2)) {
+    if(do_numastat == CONFIG_BOOLEAN_YES || (do_numastat == CONFIG_BOOLEAN_AUTO && numa_node_count >= 2)) {
         for(m = numa_root; m; m = m->next) {
             if(m->numastat_filename) {
                 if(unlikely(!m->numastat_ff)) {
@@ -92,13 +92,14 @@ int do_proc_sys_devices_system_node(int update_every, usec_t dt) {
 
                 RRDSET *st = m->numastat_st;
                 if(unlikely(!st)) {
-                    st = rrdset_create("mem", m->name, NULL, "numa", NULL, "NUMA events", "events/s", 1000, update_every, RRDSET_TYPE_LINE);
-                    st->isdetail = 1;
-
-                    rrddim_add(st, "local_node", "local", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "numa_foreign", "foreign", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "interleave_hit", "interleave", 1, 1, RRDDIM_INCREMENTAL);
-                    rrddim_add(st, "other_node", "other", 1, 1, RRDDIM_INCREMENTAL);
+                    st = rrdset_create_localhost("mem", m->name, NULL, "numa", NULL, "NUMA events", "events/s", 1000
+                                                 , update_every, RRDSET_TYPE_LINE);
+                    rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+
+                    rrddim_add(st, "local_node", "local", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "numa_foreign", "foreign", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "interleave_hit", "interleave", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                    rrddim_add(st, "other_node", "other", 1, 1, RRD_ALGORITHM_INCREMENTAL);
 
                     m->numastat_st = st;
                 }
index 8b92646b7442e54708c56c3c32f5a05c8769c361..7981804a6253198a3471d9fe2d79b390162f9f43 100644 (file)
@@ -8,22 +8,22 @@
 
 static long system_page_size = 4096; // system will be queried via sysconf() in configuration()
 
-static int cgroup_enable_cpuacct_stat = CONFIG_ONDEMAND_ONDEMAND;
-static int cgroup_enable_cpuacct_usage = CONFIG_ONDEMAND_ONDEMAND;
-static int cgroup_enable_memory = CONFIG_ONDEMAND_ONDEMAND;
-static int cgroup_enable_detailed_memory = CONFIG_ONDEMAND_ONDEMAND;
-static int cgroup_enable_memory_failcnt = CONFIG_ONDEMAND_ONDEMAND;
-static int cgroup_enable_swap = CONFIG_ONDEMAND_ONDEMAND;
-static int cgroup_enable_blkio_io = CONFIG_ONDEMAND_ONDEMAND;
-static int cgroup_enable_blkio_ops = CONFIG_ONDEMAND_ONDEMAND;
-static int cgroup_enable_blkio_throttle_io = CONFIG_ONDEMAND_ONDEMAND;
-static int cgroup_enable_blkio_throttle_ops = CONFIG_ONDEMAND_ONDEMAND;
-static int cgroup_enable_blkio_merged_ops = CONFIG_ONDEMAND_ONDEMAND;
-static int cgroup_enable_blkio_queued_ops = CONFIG_ONDEMAND_ONDEMAND;
-
-static int cgroup_enable_systemd_services = CONFIG_ONDEMAND_YES;
-static int cgroup_enable_systemd_services_detailed_memory = CONFIG_ONDEMAND_NO;
-static int cgroup_used_memory_without_cache = CONFIG_ONDEMAND_YES;
+static int cgroup_enable_cpuacct_stat = CONFIG_BOOLEAN_AUTO;
+static int cgroup_enable_cpuacct_usage = CONFIG_BOOLEAN_AUTO;
+static int cgroup_enable_memory = CONFIG_BOOLEAN_AUTO;
+static int cgroup_enable_detailed_memory = CONFIG_BOOLEAN_AUTO;
+static int cgroup_enable_memory_failcnt = CONFIG_BOOLEAN_AUTO;
+static int cgroup_enable_swap = CONFIG_BOOLEAN_AUTO;
+static int cgroup_enable_blkio_io = CONFIG_BOOLEAN_AUTO;
+static int cgroup_enable_blkio_ops = CONFIG_BOOLEAN_AUTO;
+static int cgroup_enable_blkio_throttle_io = CONFIG_BOOLEAN_AUTO;
+static int cgroup_enable_blkio_throttle_ops = CONFIG_BOOLEAN_AUTO;
+static int cgroup_enable_blkio_merged_ops = CONFIG_BOOLEAN_AUTO;
+static int cgroup_enable_blkio_queued_ops = CONFIG_BOOLEAN_AUTO;
+
+static int cgroup_enable_systemd_services = CONFIG_BOOLEAN_YES;
+static int cgroup_enable_systemd_services_detailed_memory = CONFIG_BOOLEAN_NO;
+static int cgroup_used_memory_without_cache = CONFIG_BOOLEAN_YES;
 
 static int cgroup_search_in_devices = 1;
 
@@ -64,9 +64,9 @@ void read_cgroup_plugin_configuration() {
     user_hash = simple_hash("user");
     system_hash = simple_hash("system");
 
-    cgroup_update_every = (int)config_get_number("plugin:cgroups", "update every", rrd_update_every);
-    if(cgroup_update_every < rrd_update_every)
-        cgroup_update_every = rrd_update_every;
+    cgroup_update_every = (int)config_get_number("plugin:cgroups", "update every", localhost->rrd_update_every);
+    if(cgroup_update_every < localhost->rrd_update_every)
+        cgroup_update_every = localhost->rrd_update_every;
 
     cgroup_check_for_new_every = (int)config_get_number("plugin:cgroups", "check for new cgroups every", cgroup_check_for_new_every * cgroup_update_every);
     if(cgroup_check_for_new_every < cgroup_update_every)
@@ -211,7 +211,7 @@ void read_cgroup_plugin_configuration() {
 
 struct blkio {
     int updated;
-    int enabled; // CONFIG_ONDEMAND_YES or CONFIG_ONDEMAND_ONDEMAND
+    int enabled; // CONFIG_BOOLEAN_YES or CONFIG_BOOLEAN_AUTO
     int delay_counter;
 
     char *filename;
@@ -236,10 +236,10 @@ struct memory {
     int updated_msw_usage_in_bytes;
     int updated_failcnt;
 
-    int enabled_detailed;           // CONFIG_ONDEMAND_YES or CONFIG_ONDEMAND_ONDEMAND
-    int enabled_usage_in_bytes;     // CONFIG_ONDEMAND_YES or CONFIG_ONDEMAND_ONDEMAND
-    int enabled_msw_usage_in_bytes; // CONFIG_ONDEMAND_YES or CONFIG_ONDEMAND_ONDEMAND
-    int enabled_failcnt;            // CONFIG_ONDEMAND_YES or CONFIG_ONDEMAND_ONDEMAND
+    int enabled_detailed;           // CONFIG_BOOLEAN_YES or CONFIG_BOOLEAN_AUTO
+    int enabled_usage_in_bytes;     // CONFIG_BOOLEAN_YES or CONFIG_BOOLEAN_AUTO
+    int enabled_msw_usage_in_bytes; // CONFIG_BOOLEAN_YES or CONFIG_BOOLEAN_AUTO
+    int enabled_failcnt;            // CONFIG_BOOLEAN_YES or CONFIG_BOOLEAN_AUTO
 
     int delay_counter_detailed;
     int delay_counter_failcnt;
@@ -298,7 +298,7 @@ struct memory {
 // https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt
 struct cpuacct_stat {
     int updated;
-    int enabled; // CONFIG_ONDEMAND_YES or CONFIG_ONDEMAND_ONDEMAND
+    int enabled; // CONFIG_BOOLEAN_YES or CONFIG_BOOLEAN_AUTO
 
     char *filename;
 
@@ -309,7 +309,7 @@ struct cpuacct_stat {
 // https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt
 struct cpuacct_usage {
     int updated;
-    int enabled; // CONFIG_ONDEMAND_YES or CONFIG_ONDEMAND_ONDEMAND
+    int enabled; // CONFIG_BOOLEAN_YES or CONFIG_BOOLEAN_AUTO
 
     char *filename;
 
@@ -437,8 +437,8 @@ static inline void cgroup_read_cpuacct_stat(struct cpuacct_stat *cp) {
 
         cp->updated = 1;
 
-        if(unlikely(cp->enabled == CONFIG_ONDEMAND_ONDEMAND && (cp->user || cp->system)))
-            cp->enabled = CONFIG_ONDEMAND_YES;
+        if(unlikely(cp->enabled == CONFIG_BOOLEAN_AUTO && (cp->user || cp->system)))
+            cp->enabled = CONFIG_BOOLEAN_YES;
     }
 }
 
@@ -492,15 +492,15 @@ static inline void cgroup_read_cpuacct_usage(struct cpuacct_usage *ca) {
 
         ca->updated = 1;
 
-        if(unlikely(ca->enabled == CONFIG_ONDEMAND_ONDEMAND && total))
-            ca->enabled = CONFIG_ONDEMAND_YES;
+        if(unlikely(ca->enabled == CONFIG_BOOLEAN_AUTO && total))
+            ca->enabled = CONFIG_BOOLEAN_YES;
     }
 }
 
 static inline void cgroup_read_blkio(struct blkio *io) {
     static procfile *ff = NULL;
 
-    if(unlikely(io->enabled == CONFIG_ONDEMAND_ONDEMAND && io->delay_counter > 0)) {
+    if(unlikely(io->enabled == CONFIG_BOOLEAN_AUTO && io->delay_counter > 0)) {
         io->delay_counter--;
         return;
     }
@@ -558,9 +558,9 @@ static inline void cgroup_read_blkio(struct blkio *io) {
 
         io->updated = 1;
 
-        if(unlikely(io->enabled == CONFIG_ONDEMAND_ONDEMAND)) {
+        if(unlikely(io->enabled == CONFIG_BOOLEAN_AUTO)) {
             if(unlikely(io->Read || io->Write))
-                io->enabled = CONFIG_ONDEMAND_YES;
+                io->enabled = CONFIG_BOOLEAN_YES;
             else
                 io->delay_counter = cgroup_recheck_zero_blkio_every_iterations;
         }
@@ -572,7 +572,7 @@ static inline void cgroup_read_memory(struct memory *mem) {
 
     // read detailed ram usage
     if(likely(mem->filename_detailed)) {
-        if(unlikely(mem->enabled_detailed == CONFIG_ONDEMAND_ONDEMAND && mem->delay_counter_detailed > 0)) {
+        if(unlikely(mem->enabled_detailed == CONFIG_BOOLEAN_AUTO && mem->delay_counter_detailed > 0)) {
             mem->delay_counter_detailed--;
             goto memory_next;
         }
@@ -631,9 +631,9 @@ static inline void cgroup_read_memory(struct memory *mem) {
 
         mem->updated_detailed = 1;
 
-        if(unlikely(mem->enabled_detailed == CONFIG_ONDEMAND_ONDEMAND)) {
+        if(unlikely(mem->enabled_detailed == CONFIG_BOOLEAN_AUTO)) {
             if(mem->cache || mem->dirty || mem->rss || mem->rss_huge || mem->mapped_file || mem->writeback || mem->swap || mem->pgpgin || mem->pgpgout || mem->pgfault || mem->pgmajfault)
-                mem->enabled_detailed = CONFIG_ONDEMAND_YES;
+                mem->enabled_detailed = CONFIG_BOOLEAN_YES;
             else
                 mem->delay_counter_detailed = cgroup_recheck_zero_mem_detailed_every_iterations;
         }
@@ -644,30 +644,30 @@ memory_next:
     // read usage_in_bytes
     if(likely(mem->filename_usage_in_bytes)) {
         mem->updated_usage_in_bytes = !read_single_number_file(mem->filename_usage_in_bytes, &mem->usage_in_bytes);
-        if(unlikely(mem->updated_usage_in_bytes && mem->enabled_usage_in_bytes == CONFIG_ONDEMAND_ONDEMAND && mem->usage_in_bytes))
-            mem->enabled_usage_in_bytes = CONFIG_ONDEMAND_YES;
+        if(unlikely(mem->updated_usage_in_bytes && mem->enabled_usage_in_bytes == CONFIG_BOOLEAN_AUTO && mem->usage_in_bytes))
+            mem->enabled_usage_in_bytes = CONFIG_BOOLEAN_YES;
     }
 
     // read msw_usage_in_bytes
     if(likely(mem->filename_msw_usage_in_bytes)) {
         mem->updated_msw_usage_in_bytes = !read_single_number_file(mem->filename_msw_usage_in_bytes, &mem->msw_usage_in_bytes);
-        if(unlikely(mem->updated_msw_usage_in_bytes && mem->enabled_msw_usage_in_bytes == CONFIG_ONDEMAND_ONDEMAND && mem->msw_usage_in_bytes))
-            mem->enabled_msw_usage_in_bytes = CONFIG_ONDEMAND_YES;
+        if(unlikely(mem->updated_msw_usage_in_bytes && mem->enabled_msw_usage_in_bytes == CONFIG_BOOLEAN_AUTO && mem->msw_usage_in_bytes))
+            mem->enabled_msw_usage_in_bytes = CONFIG_BOOLEAN_YES;
     }
 
     // read failcnt
     if(likely(mem->filename_failcnt)) {
-        if(unlikely(mem->enabled_failcnt == CONFIG_ONDEMAND_ONDEMAND && mem->delay_counter_failcnt > 0)) {
+        if(unlikely(mem->enabled_failcnt == CONFIG_BOOLEAN_AUTO && mem->delay_counter_failcnt > 0)) {
             mem->updated_failcnt = 0;
             mem->delay_counter_failcnt--;
         }
         else {
             mem->updated_failcnt = !read_single_number_file(mem->filename_failcnt, &mem->failcnt);
-            if(unlikely(mem->updated_failcnt && mem->enabled_failcnt == CONFIG_ONDEMAND_ONDEMAND)) {
+            if(unlikely(mem->updated_failcnt && mem->enabled_failcnt == CONFIG_BOOLEAN_AUTO)) {
                 if(unlikely(!mem->failcnt))
                     mem->delay_counter_failcnt = cgroup_recheck_zero_mem_failcnt_every_iterations;
                 else
-                    mem->enabled_failcnt = CONFIG_ONDEMAND_YES;
+                    mem->enabled_failcnt = CONFIG_BOOLEAN_YES;
             }
         }
     }
@@ -1074,7 +1074,7 @@ static inline void find_all_cgroups() {
     if(cgroup_enable_cpuacct_stat || cgroup_enable_cpuacct_usage) {
         if(find_dir_in_subdirs(cgroup_cpuacct_base, NULL, found_subdir_in_dir) == -1) {
             cgroup_enable_cpuacct_stat =
-            cgroup_enable_cpuacct_usage = CONFIG_ONDEMAND_NO;
+            cgroup_enable_cpuacct_usage = CONFIG_BOOLEAN_NO;
             error("disabled CGROUP cpu statistics.");
         }
     }
@@ -1086,7 +1086,7 @@ static inline void find_all_cgroups() {
             cgroup_enable_blkio_throttle_io =
             cgroup_enable_blkio_throttle_ops =
             cgroup_enable_blkio_merged_ops =
-            cgroup_enable_blkio_queued_ops = CONFIG_ONDEMAND_NO;
+            cgroup_enable_blkio_queued_ops = CONFIG_BOOLEAN_NO;
             error("disabled CGROUP blkio statistics.");
         }
     }
@@ -1096,7 +1096,7 @@ static inline void find_all_cgroups() {
             cgroup_enable_memory =
             cgroup_enable_detailed_memory =
             cgroup_enable_swap =
-            cgroup_enable_memory_failcnt = CONFIG_ONDEMAND_NO;
+            cgroup_enable_memory_failcnt = CONFIG_BOOLEAN_NO;
             error("disabled CGROUP memory statistics.");
         }
     }
@@ -1150,7 +1150,7 @@ static inline void find_all_cgroups() {
             snprintfz(filename, FILENAME_MAX, "%s%s/memory.stat", cgroup_memory_base, cg->id);
             if(likely(stat(filename, &buf) != -1)) {
                 cg->memory.filename_detailed = strdupz(filename);
-                cg->memory.enabled_detailed = (cgroup_enable_detailed_memory == CONFIG_ONDEMAND_YES)?CONFIG_ONDEMAND_YES:CONFIG_ONDEMAND_ONDEMAND;
+                cg->memory.enabled_detailed = (cgroup_enable_detailed_memory == CONFIG_BOOLEAN_YES)?CONFIG_BOOLEAN_YES:CONFIG_BOOLEAN_AUTO;
                 debug(D_CGROUP, "memory.stat filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_detailed);
             }
             else
@@ -1314,10 +1314,11 @@ void update_services_charts(int update_every,
         if(unlikely(!st_cpu)) {
             char title[CHART_TITLE_MAX + 1];
 
-            st_cpu = rrdset_find_bytype("services", "cpu");
+            st_cpu = rrdset_find_bytype_localhost("services", "cpu");
             if(likely(!st_cpu)) {
                 snprintfz(title, CHART_TITLE_MAX, "Systemd Services CPU utilization (%d%% = %d core%s)", (processors * 100), processors, (processors > 1) ? "s" : "");
-                st_cpu = rrdset_create("services", "cpu", NULL, "cpu", "services.cpu", title, "%", CHART_PRIORITY_SYSTEMD_SERVICES, update_every, RRDSET_TYPE_STACKED);
+                st_cpu = rrdset_create_localhost("services", "cpu", NULL, "cpu", "services.cpu", title, "%"
+                                                 , CHART_PRIORITY_SYSTEMD_SERVICES, update_every, RRDSET_TYPE_STACKED);
             }
         }
         else
@@ -1326,9 +1327,13 @@ void update_services_charts(int update_every,
 
     if(likely(do_mem_usage)) {
         if(unlikely(!st_mem_usage)) {
-            st_mem_usage = rrdset_find_bytype("services", "mem_usage");
+            st_mem_usage = rrdset_find_bytype_localhost("services", "mem_usage");
             if(likely(!st_mem_usage))
-                st_mem_usage = rrdset_create("services", "mem_usage", NULL, "mem", "services.mem_usage", (cgroup_used_memory_without_cache)?"Systemd Services Used Memory without Cache":"Systemd Services Used Memory", "MB", CHART_PRIORITY_SYSTEMD_SERVICES + 10, update_every, RRDSET_TYPE_STACKED);
+                st_mem_usage = rrdset_create_localhost("services", "mem_usage", NULL, "mem", "services.mem_usage"
+                                                       , (cgroup_used_memory_without_cache)
+                                                         ? "Systemd Services Used Memory without Cache"
+                                                         : "Systemd Services Used Memory", "MB",
+                        CHART_PRIORITY_SYSTEMD_SERVICES + 10, update_every, RRDSET_TYPE_STACKED);
         }
         else
             rrdset_next(st_mem_usage);
@@ -1336,65 +1341,90 @@ void update_services_charts(int update_every,
 
     if(likely(do_mem_detailed)) {
         if(unlikely(!st_mem_detailed_rss)) {
-            st_mem_detailed_rss = rrdset_find_bytype("services", "mem_rss");
+            st_mem_detailed_rss = rrdset_find_bytype_localhost("services", "mem_rss");
             if(likely(!st_mem_detailed_rss))
-                st_mem_detailed_rss = rrdset_create("services", "mem_rss", NULL, "mem", "services.mem_rss", "Systemd Services RSS Memory", "MB", CHART_PRIORITY_SYSTEMD_SERVICES + 20, update_every, RRDSET_TYPE_STACKED);
+                st_mem_detailed_rss = rrdset_create_localhost("services", "mem_rss", NULL, "mem", "services.mem_rss"
+                                                              , "Systemd Services RSS Memory", "MB",
+                        CHART_PRIORITY_SYSTEMD_SERVICES + 20, update_every, RRDSET_TYPE_STACKED);
         }
         else
             rrdset_next(st_mem_detailed_rss);
 
         if(unlikely(!st_mem_detailed_mapped)) {
-            st_mem_detailed_mapped = rrdset_find_bytype("services", "mem_mapped");
+            st_mem_detailed_mapped = rrdset_find_bytype_localhost("services", "mem_mapped");
             if(likely(!st_mem_detailed_mapped))
-                st_mem_detailed_mapped = rrdset_create("services", "mem_mapped", NULL, "mem", "services.mem_mapped", "Systemd Services Mapped Memory", "MB", CHART_PRIORITY_SYSTEMD_SERVICES + 30, update_every, RRDSET_TYPE_STACKED);
+                st_mem_detailed_mapped = rrdset_create_localhost("services", "mem_mapped", NULL, "mem"
+                                                                 , "services.mem_mapped"
+                                                                 , "Systemd Services Mapped Memory", "MB",
+                        CHART_PRIORITY_SYSTEMD_SERVICES + 30, update_every, RRDSET_TYPE_STACKED);
         }
         else
             rrdset_next(st_mem_detailed_mapped);
 
         if(unlikely(!st_mem_detailed_cache)) {
-            st_mem_detailed_cache = rrdset_find_bytype("services", "mem_cache");
+            st_mem_detailed_cache = rrdset_find_bytype_localhost("services", "mem_cache");
             if(likely(!st_mem_detailed_cache))
-                st_mem_detailed_cache = rrdset_create("services", "mem_cache", NULL, "mem", "services.mem_cache", "Systemd Services Cache Memory", "MB", CHART_PRIORITY_SYSTEMD_SERVICES + 40, update_every, RRDSET_TYPE_STACKED);
+                st_mem_detailed_cache = rrdset_create_localhost("services", "mem_cache", NULL, "mem"
+                                                                , "services.mem_cache", "Systemd Services Cache Memory"
+                                                                , "MB", CHART_PRIORITY_SYSTEMD_SERVICES + 40
+                                                                , update_every, RRDSET_TYPE_STACKED);
         }
         else
             rrdset_next(st_mem_detailed_cache);
 
         if(unlikely(!st_mem_detailed_writeback)) {
-            st_mem_detailed_writeback = rrdset_find_bytype("services", "mem_writeback");
+            st_mem_detailed_writeback = rrdset_find_bytype_localhost("services", "mem_writeback");
             if(likely(!st_mem_detailed_writeback))
-                st_mem_detailed_writeback = rrdset_create("services", "mem_writeback", NULL, "mem", "services.mem_writeback", "Systemd Services Writeback Memory", "MB", CHART_PRIORITY_SYSTEMD_SERVICES + 50, update_every, RRDSET_TYPE_STACKED);
+                st_mem_detailed_writeback = rrdset_create_localhost("services", "mem_writeback", NULL, "mem"
+                                                                    , "services.mem_writeback"
+                                                                    , "Systemd Services Writeback Memory", "MB",
+                        CHART_PRIORITY_SYSTEMD_SERVICES + 50, update_every, RRDSET_TYPE_STACKED);
         }
         else
             rrdset_next(st_mem_detailed_writeback);
 
         if(unlikely(!st_mem_detailed_pgfault)) {
-            st_mem_detailed_pgfault = rrdset_find_bytype("services", "mem_pgfault");
+            st_mem_detailed_pgfault = rrdset_find_bytype_localhost("services", "mem_pgfault");
             if(likely(!st_mem_detailed_pgfault))
-                st_mem_detailed_pgfault = rrdset_create("services", "mem_pgfault", NULL, "mem", "services.mem_pgfault", "Systemd Services Memory Minor Page Faults", "MB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 60, update_every, RRDSET_TYPE_STACKED);
+                st_mem_detailed_pgfault = rrdset_create_localhost("services", "mem_pgfault", NULL, "mem"
+                                                                  , "services.mem_pgfault"
+                                                                  , "Systemd Services Memory Minor Page Faults", "MB/s",
+                        CHART_PRIORITY_SYSTEMD_SERVICES + 60, update_every, RRDSET_TYPE_STACKED);
         }
         else
             rrdset_next(st_mem_detailed_pgfault);
 
         if(unlikely(!st_mem_detailed_pgmajfault)) {
-            st_mem_detailed_pgmajfault = rrdset_find_bytype("services", "mem_pgmajfault");
+            st_mem_detailed_pgmajfault = rrdset_find_bytype_localhost("services", "mem_pgmajfault");
             if(likely(!st_mem_detailed_pgmajfault))
-                st_mem_detailed_pgmajfault = rrdset_create("services", "mem_pgmajfault", NULL, "mem", "services.mem_pgmajfault", "Systemd Services Memory Major Page Faults", "MB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 70, update_every, RRDSET_TYPE_STACKED);
+                st_mem_detailed_pgmajfault = rrdset_create_localhost("services", "mem_pgmajfault", NULL, "mem"
+                                                                     , "services.mem_pgmajfault"
+                                                                     , "Systemd Services Memory Major Page Faults"
+                                                                     , "MB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 70
+                                                                     , update_every, RRDSET_TYPE_STACKED);
         }
         else
             rrdset_next(st_mem_detailed_pgmajfault);
 
         if(unlikely(!st_mem_detailed_pgpgin)) {
-            st_mem_detailed_pgpgin = rrdset_find_bytype("services", "mem_pgpgin");
+            st_mem_detailed_pgpgin = rrdset_find_bytype_localhost("services", "mem_pgpgin");
             if(likely(!st_mem_detailed_pgpgin))
-                st_mem_detailed_pgpgin = rrdset_create("services", "mem_pgpgin", NULL, "mem", "services.mem_pgpgin", "Systemd Services Memory Charging Activity", "MB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 80, update_every, RRDSET_TYPE_STACKED);
+                st_mem_detailed_pgpgin = rrdset_create_localhost("services", "mem_pgpgin", NULL, "mem"
+                                                                 , "services.mem_pgpgin"
+                                                                 , "Systemd Services Memory Charging Activity", "MB/s",
+                        CHART_PRIORITY_SYSTEMD_SERVICES + 80, update_every, RRDSET_TYPE_STACKED);
         }
         else
             rrdset_next(st_mem_detailed_pgpgin);
 
         if(unlikely(!st_mem_detailed_pgpgout)) {
-            st_mem_detailed_pgpgout = rrdset_find_bytype("services", "mem_pgpgout");
+            st_mem_detailed_pgpgout = rrdset_find_bytype_localhost("services", "mem_pgpgout");
             if(likely(!st_mem_detailed_pgpgout))
-                st_mem_detailed_pgpgout = rrdset_create("services", "mem_pgpgout", NULL, "mem", "services.mem_pgpgout", "Systemd Services Memory Uncharging Activity", "MB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 90, update_every, RRDSET_TYPE_STACKED);
+                st_mem_detailed_pgpgout = rrdset_create_localhost("services", "mem_pgpgout", NULL, "mem"
+                                                                  , "services.mem_pgpgout"
+                                                                  , "Systemd Services Memory Uncharging Activity"
+                                                                  , "MB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 90
+                                                                  , update_every, RRDSET_TYPE_STACKED);
         }
         else
             rrdset_next(st_mem_detailed_pgpgout);
@@ -1402,9 +1432,11 @@ void update_services_charts(int update_every,
 
     if(likely(do_mem_failcnt)) {
         if(unlikely(!st_mem_failcnt)) {
-            st_mem_failcnt = rrdset_find_bytype("services", "mem_failcnt");
+            st_mem_failcnt = rrdset_find_bytype_localhost("services", "mem_failcnt");
             if(likely(!st_mem_failcnt))
-                st_mem_failcnt = rrdset_create("services", "mem_failcnt", NULL, "mem", "services.mem_failcnt", "Systemd Services Memory Limit Failures", "MB", CHART_PRIORITY_SYSTEMD_SERVICES + 110, update_every, RRDSET_TYPE_STACKED);
+                st_mem_failcnt = rrdset_create_localhost("services", "mem_failcnt", NULL, "mem", "services.mem_failcnt"
+                                                         , "Systemd Services Memory Limit Failures", "MB",
+                        CHART_PRIORITY_SYSTEMD_SERVICES + 110, update_every, RRDSET_TYPE_STACKED);
         }
         else
             rrdset_next(st_mem_failcnt);
@@ -1412,9 +1444,11 @@ void update_services_charts(int update_every,
 
     if(likely(do_swap_usage)) {
         if(unlikely(!st_swap_usage)) {
-            st_swap_usage = rrdset_find_bytype("services", "swap_usage");
+            st_swap_usage = rrdset_find_bytype_localhost("services", "swap_usage");
             if(likely(!st_swap_usage))
-                st_swap_usage = rrdset_create("services", "swap_usage", NULL, "swap", "services.swap_usage", "Systemd Services Swap Memory Used", "MB", CHART_PRIORITY_SYSTEMD_SERVICES + 100, update_every, RRDSET_TYPE_STACKED);
+                st_swap_usage = rrdset_create_localhost("services", "swap_usage", NULL, "swap", "services.swap_usage"
+                                                        , "Systemd Services Swap Memory Used", "MB",
+                        CHART_PRIORITY_SYSTEMD_SERVICES + 100, update_every, RRDSET_TYPE_STACKED);
         }
         else
             rrdset_next(st_swap_usage);
@@ -1422,17 +1456,21 @@ void update_services_charts(int update_every,
 
     if(likely(do_io)) {
         if(unlikely(!st_io_read)) {
-            st_io_read = rrdset_find_bytype("services", "io_read");
+            st_io_read = rrdset_find_bytype_localhost("services", "io_read");
             if(likely(!st_io_read))
-                st_io_read = rrdset_create("services", "io_read", NULL, "disk", "services.io_read", "Systemd Services Disk Read Bandwidth", "KB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 120, update_every, RRDSET_TYPE_STACKED);
+                st_io_read = rrdset_create_localhost("services", "io_read", NULL, "disk", "services.io_read"
+                                                     , "Systemd Services Disk Read Bandwidth", "KB/s",
+                        CHART_PRIORITY_SYSTEMD_SERVICES + 120, update_every, RRDSET_TYPE_STACKED);
         }
         else
             rrdset_next(st_io_read);
 
         if(unlikely(!st_io_write)) {
-            st_io_write = rrdset_find_bytype("services", "io_write");
+            st_io_write = rrdset_find_bytype_localhost("services", "io_write");
             if(likely(!st_io_write))
-                st_io_write = rrdset_create("services", "io_write", NULL, "disk", "services.io_write", "Systemd Services Disk Write Bandwidth", "KB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 130, update_every, RRDSET_TYPE_STACKED);
+                st_io_write = rrdset_create_localhost("services", "io_write", NULL, "disk", "services.io_write"
+                                                      , "Systemd Services Disk Write Bandwidth", "KB/s",
+                        CHART_PRIORITY_SYSTEMD_SERVICES + 130, update_every, RRDSET_TYPE_STACKED);
         }
         else
             rrdset_next(st_io_write);
@@ -1440,17 +1478,24 @@ void update_services_charts(int update_every,
 
     if(likely(do_io_ops)) {
         if(unlikely(!st_io_serviced_read)) {
-            st_io_serviced_read = rrdset_find_bytype("services", "io_ops_read");
+            st_io_serviced_read = rrdset_find_bytype_localhost("services", "io_ops_read");
             if(likely(!st_io_serviced_read))
-                st_io_serviced_read = rrdset_create("services", "io_ops_read", NULL, "disk", "services.io_ops_read", "Systemd Services Disk Read Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 140, update_every, RRDSET_TYPE_STACKED);
+                st_io_serviced_read = rrdset_create_localhost("services", "io_ops_read", NULL, "disk"
+                                                              , "services.io_ops_read"
+                                                              , "Systemd Services Disk Read Operations", "operations/s",
+                        CHART_PRIORITY_SYSTEMD_SERVICES + 140, update_every, RRDSET_TYPE_STACKED);
         }
         else
             rrdset_next(st_io_serviced_read);
 
         if(unlikely(!st_io_serviced_write)) {
-            st_io_serviced_write = rrdset_find_bytype("services", "io_ops_write");
+            st_io_serviced_write = rrdset_find_bytype_localhost("services", "io_ops_write");
             if(likely(!st_io_serviced_write))
-                st_io_serviced_write = rrdset_create("services", "io_ops_write", NULL, "disk", "services.io_ops_write", "Systemd Services Disk Write Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 150, update_every, RRDSET_TYPE_STACKED);
+                st_io_serviced_write = rrdset_create_localhost("services", "io_ops_write", NULL, "disk"
+                                                               , "services.io_ops_write"
+                                                               , "Systemd Services Disk Write Operations"
+                                                               , "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 150
+                                                               , update_every, RRDSET_TYPE_STACKED);
         }
         else
             rrdset_next(st_io_serviced_write);
@@ -1458,17 +1503,24 @@ void update_services_charts(int update_every,
 
     if(likely(do_throttle_io)) {
         if(unlikely(!st_throttle_io_read)) {
-            st_throttle_io_read = rrdset_find_bytype("services", "throttle_io_read");
+            st_throttle_io_read = rrdset_find_bytype_localhost("services", "throttle_io_read");
             if(likely(!st_throttle_io_read))
-                st_throttle_io_read = rrdset_create("services", "throttle_io_read", NULL, "disk", "services.throttle_io_read", "Systemd Services Throttle Disk Read Bandwidth", "KB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 160, update_every, RRDSET_TYPE_STACKED);
+                st_throttle_io_read = rrdset_create_localhost("services", "throttle_io_read", NULL, "disk"
+                                                              , "services.throttle_io_read"
+                                                              , "Systemd Services Throttle Disk Read Bandwidth", "KB/s",
+                        CHART_PRIORITY_SYSTEMD_SERVICES + 160, update_every, RRDSET_TYPE_STACKED);
         }
         else
             rrdset_next(st_throttle_io_read);
 
         if(unlikely(!st_throttle_io_write)) {
-            st_throttle_io_write = rrdset_find_bytype("services", "throttle_io_write");
+            st_throttle_io_write = rrdset_find_bytype_localhost("services", "throttle_io_write");
             if(likely(!st_throttle_io_write))
-                st_throttle_io_write = rrdset_create("services", "throttle_io_write", NULL, "disk", "services.throttle_io_write", "Systemd Services Throttle Disk Write Bandwidth", "KB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 170, update_every, RRDSET_TYPE_STACKED);
+                st_throttle_io_write = rrdset_create_localhost("services", "throttle_io_write", NULL, "disk"
+                                                               , "services.throttle_io_write"
+                                                               , "Systemd Services Throttle Disk Write Bandwidth"
+                                                               , "KB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 170
+                                                               , update_every, RRDSET_TYPE_STACKED);
         }
         else
             rrdset_next(st_throttle_io_write);
@@ -1476,17 +1528,25 @@ void update_services_charts(int update_every,
 
     if(likely(do_throttle_ops)) {
         if(unlikely(!st_throttle_ops_read)) {
-            st_throttle_ops_read = rrdset_find_bytype("services", "throttle_io_ops_read");
+            st_throttle_ops_read = rrdset_find_bytype_localhost("services", "throttle_io_ops_read");
             if(likely(!st_throttle_ops_read))
-                st_throttle_ops_read = rrdset_create("services", "throttle_io_ops_read", NULL, "disk", "services.throttle_io_ops_read", "Systemd Services Throttle Disk Read Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 180, update_every, RRDSET_TYPE_STACKED);
+                st_throttle_ops_read = rrdset_create_localhost("services", "throttle_io_ops_read", NULL, "disk"
+                                                               , "services.throttle_io_ops_read"
+                                                               , "Systemd Services Throttle Disk Read Operations"
+                                                               , "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 180
+                                                               , update_every, RRDSET_TYPE_STACKED);
         }
         else
             rrdset_next(st_throttle_ops_read);
 
         if(unlikely(!st_throttle_ops_write)) {
-            st_throttle_ops_write = rrdset_find_bytype("services", "throttle_io_ops_write");
+            st_throttle_ops_write = rrdset_find_bytype_localhost("services", "throttle_io_ops_write");
             if(likely(!st_throttle_ops_write))
-                st_throttle_ops_write = rrdset_create("services", "throttle_io_ops_write", NULL, "disk", "services.throttle_io_ops_write", "Systemd Services Throttle Disk Write Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 190, update_every, RRDSET_TYPE_STACKED);
+                st_throttle_ops_write = rrdset_create_localhost("services", "throttle_io_ops_write", NULL, "disk"
+                                                                , "services.throttle_io_ops_write"
+                                                                , "Systemd Services Throttle Disk Write Operations"
+                                                                , "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 190
+                                                                , update_every, RRDSET_TYPE_STACKED);
         }
         else
             rrdset_next(st_throttle_ops_write);
@@ -1494,17 +1554,25 @@ void update_services_charts(int update_every,
 
     if(likely(do_queued_ops)) {
         if(unlikely(!st_queued_ops_read)) {
-            st_queued_ops_read = rrdset_find_bytype("services", "queued_io_ops_read");
+            st_queued_ops_read = rrdset_find_bytype_localhost("services", "queued_io_ops_read");
             if(likely(!st_queued_ops_read))
-                st_queued_ops_read = rrdset_create("services", "queued_io_ops_read", NULL, "disk", "services.queued_io_ops_read", "Systemd Services Queued Disk Read Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 200, update_every, RRDSET_TYPE_STACKED);
+                st_queued_ops_read = rrdset_create_localhost("services", "queued_io_ops_read", NULL, "disk"
+                                                             , "services.queued_io_ops_read"
+                                                             , "Systemd Services Queued Disk Read Operations"
+                                                             , "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 200
+                                                             , update_every, RRDSET_TYPE_STACKED);
         }
         else
             rrdset_next(st_queued_ops_read);
 
         if(unlikely(!st_queued_ops_write)) {
-            st_queued_ops_write = rrdset_find_bytype("services", "queued_io_ops_write");
+            st_queued_ops_write = rrdset_find_bytype_localhost("services", "queued_io_ops_write");
             if(likely(!st_queued_ops_write))
-                st_queued_ops_write = rrdset_create("services", "queued_io_ops_write", NULL, "disk", "services.queued_io_ops_write", "Systemd Services Queued Disk Write Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 210, update_every, RRDSET_TYPE_STACKED);
+                st_queued_ops_write = rrdset_create_localhost("services", "queued_io_ops_write", NULL, "disk"
+                                                              , "services.queued_io_ops_write"
+                                                              , "Systemd Services Queued Disk Write Operations"
+                                                              , "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 210
+                                                              , update_every, RRDSET_TYPE_STACKED);
         }
         else
             rrdset_next(st_queued_ops_write);
@@ -1512,17 +1580,25 @@ void update_services_charts(int update_every,
 
     if(likely(do_merged_ops)) {
         if(unlikely(!st_merged_ops_read)) {
-            st_merged_ops_read = rrdset_find_bytype("services", "merged_io_ops_read");
+            st_merged_ops_read = rrdset_find_bytype_localhost("services", "merged_io_ops_read");
             if(likely(!st_merged_ops_read))
-                st_merged_ops_read = rrdset_create("services", "merged_io_ops_read", NULL, "disk", "services.merged_io_ops_read", "Systemd Services Merged Disk Read Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 220, update_every, RRDSET_TYPE_STACKED);
+                st_merged_ops_read = rrdset_create_localhost("services", "merged_io_ops_read", NULL, "disk"
+                                                             , "services.merged_io_ops_read"
+                                                             , "Systemd Services Merged Disk Read Operations"
+                                                             , "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 220
+                                                             , update_every, RRDSET_TYPE_STACKED);
         }
         else
             rrdset_next(st_merged_ops_read);
 
         if(unlikely(!st_merged_ops_write)) {
-            st_merged_ops_write = rrdset_find_bytype("services", "merged_io_ops_write");
+            st_merged_ops_write = rrdset_find_bytype_localhost("services", "merged_io_ops_write");
             if(likely(!st_merged_ops_write))
-                st_merged_ops_write = rrdset_create("services", "merged_io_ops_write", NULL, "disk", "services.merged_io_ops_write", "Systemd Services Merged Disk Write Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 230, update_every, RRDSET_TYPE_STACKED);
+                st_merged_ops_write = rrdset_create_localhost("services", "merged_io_ops_write", NULL, "disk"
+                                                              , "services.merged_io_ops_write"
+                                                              , "Systemd Services Merged Disk Write Operations"
+                                                              , "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 230
+                                                              , update_every, RRDSET_TYPE_STACKED);
         }
         else
             rrdset_next(st_merged_ops_write);
@@ -1536,134 +1612,134 @@ void update_services_charts(int update_every,
 
         if(likely(do_cpu && cg->cpuacct_stat.updated)) {
             if(unlikely(!cg->rd_cpu))
-                cg->rd_cpu = rrddim_add(st_cpu, cg->chart_id, cg->chart_title, 100, hz, RRDDIM_INCREMENTAL);
+                cg->rd_cpu = rrddim_add(st_cpu, cg->chart_id, cg->chart_title, 100, hz, RRD_ALGORITHM_INCREMENTAL);
 
             rrddim_set_by_pointer(st_cpu, cg->rd_cpu, cg->cpuacct_stat.user + cg->cpuacct_stat.system);
         }
 
         if(likely(do_mem_usage && cg->memory.updated_usage_in_bytes)) {
             if(unlikely(!cg->rd_mem_usage))
-                cg->rd_mem_usage = rrddim_add(st_mem_usage, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+                cg->rd_mem_usage = rrddim_add(st_mem_usage, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
 
             rrddim_set_by_pointer(st_mem_usage, cg->rd_mem_usage, cg->memory.usage_in_bytes - ((cgroup_used_memory_without_cache)?cg->memory.cache:0));
         }
 
         if(likely(do_mem_detailed && cg->memory.updated_detailed)) {
             if(unlikely(!cg->rd_mem_detailed_rss))
-                cg->rd_mem_detailed_rss = rrddim_add(st_mem_detailed_rss, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+                cg->rd_mem_detailed_rss = rrddim_add(st_mem_detailed_rss, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
             rrddim_set_by_pointer(st_mem_detailed_rss, cg->rd_mem_detailed_rss, cg->memory.rss + cg->memory.rss_huge);
 
             if(unlikely(!cg->rd_mem_detailed_mapped))
-                cg->rd_mem_detailed_mapped = rrddim_add(st_mem_detailed_mapped, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+                cg->rd_mem_detailed_mapped = rrddim_add(st_mem_detailed_mapped, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
             rrddim_set_by_pointer(st_mem_detailed_mapped, cg->rd_mem_detailed_mapped, cg->memory.mapped_file);
 
             if(unlikely(!cg->rd_mem_detailed_cache))
-                cg->rd_mem_detailed_cache = rrddim_add(st_mem_detailed_cache, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+                cg->rd_mem_detailed_cache = rrddim_add(st_mem_detailed_cache, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
             rrddim_set_by_pointer(st_mem_detailed_cache, cg->rd_mem_detailed_cache, cg->memory.cache);
 
             if(unlikely(!cg->rd_mem_detailed_writeback))
-                cg->rd_mem_detailed_writeback = rrddim_add(st_mem_detailed_writeback, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+                cg->rd_mem_detailed_writeback = rrddim_add(st_mem_detailed_writeback, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
             rrddim_set_by_pointer(st_mem_detailed_writeback, cg->rd_mem_detailed_writeback, cg->memory.writeback);
 
             if(unlikely(!cg->rd_mem_detailed_pgfault))
-                cg->rd_mem_detailed_pgfault = rrddim_add(st_mem_detailed_pgfault, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL);
+                cg->rd_mem_detailed_pgfault = rrddim_add(st_mem_detailed_pgfault, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
             rrddim_set_by_pointer(st_mem_detailed_pgfault, cg->rd_mem_detailed_pgfault, cg->memory.pgfault);
 
             if(unlikely(!cg->rd_mem_detailed_pgmajfault))
-                cg->rd_mem_detailed_pgmajfault = rrddim_add(st_mem_detailed_pgmajfault, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL);
+                cg->rd_mem_detailed_pgmajfault = rrddim_add(st_mem_detailed_pgmajfault, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
             rrddim_set_by_pointer(st_mem_detailed_pgmajfault, cg->rd_mem_detailed_pgmajfault, cg->memory.pgmajfault);
 
             if(unlikely(!cg->rd_mem_detailed_pgpgin))
-                cg->rd_mem_detailed_pgpgin = rrddim_add(st_mem_detailed_pgpgin, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL);
+                cg->rd_mem_detailed_pgpgin = rrddim_add(st_mem_detailed_pgpgin, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
             rrddim_set_by_pointer(st_mem_detailed_pgpgin, cg->rd_mem_detailed_pgpgin, cg->memory.pgpgin);
 
             if(unlikely(!cg->rd_mem_detailed_pgpgout))
-                cg->rd_mem_detailed_pgpgout = rrddim_add(st_mem_detailed_pgpgout, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL);
+                cg->rd_mem_detailed_pgpgout = rrddim_add(st_mem_detailed_pgpgout, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
             rrddim_set_by_pointer(st_mem_detailed_pgpgout, cg->rd_mem_detailed_pgpgout, cg->memory.pgpgout);
         }
 
         if(likely(do_mem_failcnt && cg->memory.updated_failcnt)) {
             if(unlikely(!cg->rd_mem_failcnt))
-                cg->rd_mem_failcnt = rrddim_add(st_mem_failcnt, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL);
+                cg->rd_mem_failcnt = rrddim_add(st_mem_failcnt, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL);
 
             rrddim_set_by_pointer(st_mem_failcnt, cg->rd_mem_failcnt, cg->memory.failcnt);
         }
 
         if(likely(do_swap_usage && cg->memory.updated_msw_usage_in_bytes)) {
             if(unlikely(!cg->rd_swap_usage))
-                cg->rd_swap_usage = rrddim_add(st_swap_usage, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+                cg->rd_swap_usage = rrddim_add(st_swap_usage, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
 
             rrddim_set_by_pointer(st_swap_usage, cg->rd_swap_usage, cg->memory.msw_usage_in_bytes);
         }
 
         if(likely(do_io && cg->io_service_bytes.updated)) {
             if(unlikely(!cg->rd_io_service_bytes_read))
-                cg->rd_io_service_bytes_read = rrddim_add(st_io_read, cg->chart_id, cg->chart_title, 1, 1024, RRDDIM_INCREMENTAL);
+                cg->rd_io_service_bytes_read = rrddim_add(st_io_read, cg->chart_id, cg->chart_title, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
 
             rrddim_set_by_pointer(st_io_read, cg->rd_io_service_bytes_read, cg->io_service_bytes.Read);
 
             if(unlikely(!cg->rd_io_service_bytes_write))
-                cg->rd_io_service_bytes_write = rrddim_add(st_io_write, cg->chart_id, cg->chart_title, 1, 1024, RRDDIM_INCREMENTAL);
+                cg->rd_io_service_bytes_write = rrddim_add(st_io_write, cg->chart_id, cg->chart_title, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
 
             rrddim_set_by_pointer(st_io_write, cg->rd_io_service_bytes_write, cg->io_service_bytes.Write);
         }
 
         if(likely(do_io_ops && cg->io_serviced.updated)) {
             if(unlikely(!cg->rd_io_serviced_read))
-                cg->rd_io_serviced_read = rrddim_add(st_io_serviced_read, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL);
+                cg->rd_io_serviced_read = rrddim_add(st_io_serviced_read, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL);
 
             rrddim_set_by_pointer(st_io_serviced_read, cg->rd_io_serviced_read, cg->io_serviced.Read);
 
             if(unlikely(!cg->rd_io_serviced_write))
-                cg->rd_io_serviced_write = rrddim_add(st_io_serviced_write, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL);
+                cg->rd_io_serviced_write = rrddim_add(st_io_serviced_write, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL);
 
             rrddim_set_by_pointer(st_io_serviced_write, cg->rd_io_serviced_write, cg->io_serviced.Write);
         }
 
         if(likely(do_throttle_io && cg->throttle_io_service_bytes.updated)) {
             if(unlikely(!cg->rd_throttle_io_read))
-                cg->rd_throttle_io_read = rrddim_add(st_throttle_io_read, cg->chart_id, cg->chart_title, 1, 1024, RRDDIM_INCREMENTAL);
+                cg->rd_throttle_io_read = rrddim_add(st_throttle_io_read, cg->chart_id, cg->chart_title, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
 
             rrddim_set_by_pointer(st_throttle_io_read, cg->rd_throttle_io_read, cg->throttle_io_service_bytes.Read);
 
             if(unlikely(!cg->rd_throttle_io_write))
-                cg->rd_throttle_io_write = rrddim_add(st_throttle_io_write, cg->chart_id, cg->chart_title, 1, 1024, RRDDIM_INCREMENTAL);
+                cg->rd_throttle_io_write = rrddim_add(st_throttle_io_write, cg->chart_id, cg->chart_title, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
 
             rrddim_set_by_pointer(st_throttle_io_write, cg->rd_throttle_io_write, cg->throttle_io_service_bytes.Write);
         }
 
         if(likely(do_throttle_ops && cg->throttle_io_serviced.updated)) {
             if(unlikely(!cg->rd_throttle_io_serviced_read))
-                cg->rd_throttle_io_serviced_read = rrddim_add(st_throttle_ops_read, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL);
+                cg->rd_throttle_io_serviced_read = rrddim_add(st_throttle_ops_read, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL);
 
             rrddim_set_by_pointer(st_throttle_ops_read, cg->rd_throttle_io_serviced_read, cg->throttle_io_serviced.Read);
 
             if(unlikely(!cg->rd_throttle_io_serviced_write))
-                cg->rd_throttle_io_serviced_write = rrddim_add(st_throttle_ops_write, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL);
+                cg->rd_throttle_io_serviced_write = rrddim_add(st_throttle_ops_write, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL);
 
             rrddim_set_by_pointer(st_throttle_ops_write, cg->rd_throttle_io_serviced_write, cg->throttle_io_serviced.Write);
         }
 
         if(likely(do_queued_ops && cg->io_queued.updated)) {
             if(unlikely(!cg->rd_io_queued_read))
-                cg->rd_io_queued_read = rrddim_add(st_queued_ops_read, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL);
+                cg->rd_io_queued_read = rrddim_add(st_queued_ops_read, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL);
 
             rrddim_set_by_pointer(st_queued_ops_read, cg->rd_io_queued_read, cg->io_queued.Read);
 
             if(unlikely(!cg->rd_io_queued_write))
-                cg->rd_io_queued_write = rrddim_add(st_queued_ops_write, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL);
+                cg->rd_io_queued_write = rrddim_add(st_queued_ops_write, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL);
 
             rrddim_set_by_pointer(st_queued_ops_write, cg->rd_io_queued_write, cg->io_queued.Write);
         }
 
         if(likely(do_merged_ops && cg->io_merged.updated)) {
             if(unlikely(!cg->rd_io_merged_read))
-                cg->rd_io_merged_read = rrddim_add(st_merged_ops_read, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL);
+                cg->rd_io_merged_read = rrddim_add(st_merged_ops_read, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL);
 
             rrddim_set_by_pointer(st_merged_ops_read, cg->rd_io_merged_read, cg->io_merged.Read);
 
             if(unlikely(!cg->rd_io_merged_write))
-                cg->rd_io_merged_write = rrddim_add(st_merged_ops_write, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL);
+                cg->rd_io_merged_write = rrddim_add(st_merged_ops_write, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL);
 
             rrddim_set_by_pointer(st_merged_ops_write, cg->rd_io_merged_write, cg->io_merged.Write);
         }
@@ -1760,33 +1836,35 @@ void update_cgroup_charts(int update_every) {
             continue;
 
         if(likely(cgroup_enable_systemd_services && cg->options & CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE)) {
-            if(cg->cpuacct_stat.updated && cg->cpuacct_stat.enabled == CONFIG_ONDEMAND_YES) services_do_cpu++;
+            if(cg->cpuacct_stat.updated && cg->cpuacct_stat.enabled == CONFIG_BOOLEAN_YES) services_do_cpu++;
 
             if(cgroup_enable_systemd_services_detailed_memory && cg->memory.updated_detailed && cg->memory.enabled_detailed) services_do_mem_detailed++;
-            if(cg->memory.updated_usage_in_bytes && cg->memory.enabled_usage_in_bytes == CONFIG_ONDEMAND_YES) services_do_mem_usage++;
-            if(cg->memory.updated_failcnt && cg->memory.enabled_failcnt == CONFIG_ONDEMAND_YES) services_do_mem_failcnt++;
-            if(cg->memory.updated_msw_usage_in_bytes && cg->memory.enabled_msw_usage_in_bytes == CONFIG_ONDEMAND_YES) services_do_swap_usage++;
-
-            if(cg->io_service_bytes.updated && cg->io_service_bytes.enabled == CONFIG_ONDEMAND_YES) services_do_io++;
-            if(cg->io_serviced.updated && cg->io_serviced.enabled == CONFIG_ONDEMAND_YES) services_do_io_ops++;
-            if(cg->throttle_io_service_bytes.updated && cg->throttle_io_service_bytes.enabled == CONFIG_ONDEMAND_YES) services_do_throttle_io++;
-            if(cg->throttle_io_serviced.updated && cg->throttle_io_serviced.enabled == CONFIG_ONDEMAND_YES) services_do_throttle_ops++;
-            if(cg->io_queued.updated && cg->io_queued.enabled == CONFIG_ONDEMAND_YES) services_do_queued_ops++;
-            if(cg->io_merged.updated && cg->io_merged.enabled == CONFIG_ONDEMAND_YES) services_do_merged_ops++;
+            if(cg->memory.updated_usage_in_bytes && cg->memory.enabled_usage_in_bytes == CONFIG_BOOLEAN_YES) services_do_mem_usage++;
+            if(cg->memory.updated_failcnt && cg->memory.enabled_failcnt == CONFIG_BOOLEAN_YES) services_do_mem_failcnt++;
+            if(cg->memory.updated_msw_usage_in_bytes && cg->memory.enabled_msw_usage_in_bytes == CONFIG_BOOLEAN_YES) services_do_swap_usage++;
+
+            if(cg->io_service_bytes.updated && cg->io_service_bytes.enabled == CONFIG_BOOLEAN_YES) services_do_io++;
+            if(cg->io_serviced.updated && cg->io_serviced.enabled == CONFIG_BOOLEAN_YES) services_do_io_ops++;
+            if(cg->throttle_io_service_bytes.updated && cg->throttle_io_service_bytes.enabled == CONFIG_BOOLEAN_YES) services_do_throttle_io++;
+            if(cg->throttle_io_serviced.updated && cg->throttle_io_serviced.enabled == CONFIG_BOOLEAN_YES) services_do_throttle_ops++;
+            if(cg->io_queued.updated && cg->io_queued.enabled == CONFIG_BOOLEAN_YES) services_do_queued_ops++;
+            if(cg->io_merged.updated && cg->io_merged.enabled == CONFIG_BOOLEAN_YES) services_do_merged_ops++;
             continue;
         }
 
         type[0] = '\0';
 
-        if(likely(cg->cpuacct_stat.updated && cg->cpuacct_stat.enabled == CONFIG_ONDEMAND_YES)) {
+        if(likely(cg->cpuacct_stat.updated && cg->cpuacct_stat.enabled == CONFIG_BOOLEAN_YES)) {
             if(unlikely(!cg->st_cpu)) {
-                cg->st_cpu = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "cpu");
+                cg->st_cpu = rrdset_find_bytype_localhost(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX)
+                                                          , "cpu");
                 if(likely(!cg->st_cpu)) {
                     snprintfz(title, CHART_TITLE_MAX, "CPU Usage (%d%% = %d core%s) for cgroup %s", (processors * 100), processors, (processors > 1) ? "s" : "", cg->chart_title);
-                    cg->st_cpu = rrdset_create(type, "cpu", NULL, "cpu", "cgroup.cpu", title, "%", CHART_PRIORITY_CONTAINERS, update_every, RRDSET_TYPE_STACKED);
+                    cg->st_cpu = rrdset_create_localhost(type, "cpu", NULL, "cpu", "cgroup.cpu", title, "%"
+                                                         , CHART_PRIORITY_CONTAINERS, update_every, RRDSET_TYPE_STACKED);
                 }
-                rrddim_add(cg->st_cpu, "user", NULL, 100, hz, RRDDIM_INCREMENTAL);
-                rrddim_add(cg->st_cpu, "system", NULL, 100, hz, RRDDIM_INCREMENTAL);
+                rrddim_add(cg->st_cpu, "user", NULL, 100, hz, RRD_ALGORITHM_INCREMENTAL);
+                rrddim_add(cg->st_cpu, "system", NULL, 100, hz, RRD_ALGORITHM_INCREMENTAL);
             }
             else
                 rrdset_next(cg->st_cpu);
@@ -1796,19 +1874,22 @@ void update_cgroup_charts(int update_every) {
             rrdset_done(cg->st_cpu);
         }
 
-        if(likely(cg->cpuacct_usage.updated && cg->cpuacct_usage.enabled == CONFIG_ONDEMAND_YES)) {
+        if(likely(cg->cpuacct_usage.updated && cg->cpuacct_usage.enabled == CONFIG_BOOLEAN_YES)) {
             char id[RRD_ID_LENGTH_MAX + 1];
             unsigned int i;
 
             if(unlikely(!cg->st_cpu_per_core)) {
-                cg->st_cpu_per_core = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "cpu_per_core");
+                cg->st_cpu_per_core = rrdset_find_bytype_localhost(
+                        cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "cpu_per_core");
                 if(likely(!cg->st_cpu_per_core)) {
                     snprintfz(title, CHART_TITLE_MAX, "CPU Usage (%d%% = %d core%s) Per Core for cgroup %s", (processors * 100), processors, (processors > 1) ? "s" : "", cg->chart_title);
-                    cg->st_cpu_per_core = rrdset_create(type, "cpu_per_core", NULL, "cpu", "cgroup.cpu_per_core", title, "%", CHART_PRIORITY_CONTAINERS + 100, update_every, RRDSET_TYPE_STACKED);
+                    cg->st_cpu_per_core = rrdset_create_localhost(type, "cpu_per_core", NULL, "cpu"
+                                                                  , "cgroup.cpu_per_core", title, "%",
+                            CHART_PRIORITY_CONTAINERS + 100, update_every, RRDSET_TYPE_STACKED);
                 }
                 for(i = 0; i < cg->cpuacct_usage.cpus; i++) {
                     snprintfz(id, CHART_TITLE_MAX, "cpu%u", i);
-                    rrddim_add(cg->st_cpu_per_core, id, NULL, 100, 1000000000, RRDDIM_INCREMENTAL);
+                    rrddim_add(cg->st_cpu_per_core, id, NULL, 100, 1000000000, RRD_ALGORITHM_INCREMENTAL);
                 }
             }
             else
@@ -1821,20 +1902,22 @@ void update_cgroup_charts(int update_every) {
             rrdset_done(cg->st_cpu_per_core);
         }
 
-        if(likely(cg->memory.updated_detailed && cg->memory.enabled_detailed == CONFIG_ONDEMAND_YES)) {
+        if(likely(cg->memory.updated_detailed && cg->memory.enabled_detailed == CONFIG_BOOLEAN_YES)) {
             if(unlikely(!cg->st_mem)) {
-                cg->st_mem = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem");
+                cg->st_mem = rrdset_find_bytype_localhost(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX)
+                                                          , "mem");
                 if(likely(!cg->st_mem)) {
                     snprintfz(title, CHART_TITLE_MAX, "Memory Usage for cgroup %s", cg->chart_title);
-                    cg->st_mem = rrdset_create(type, "mem", NULL, "mem", "cgroup.mem", title, "MB", CHART_PRIORITY_CONTAINERS + 210, update_every, RRDSET_TYPE_STACKED);
+                    cg->st_mem = rrdset_create_localhost(type, "mem", NULL, "mem", "cgroup.mem", title, "MB",
+                            CHART_PRIORITY_CONTAINERS + 210, update_every, RRDSET_TYPE_STACKED);
                 }
 
-                rrddim_add(cg->st_mem, "cache", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
-                rrddim_add(cg->st_mem, "rss", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+                rrddim_add(cg->st_mem, "cache", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
+                rrddim_add(cg->st_mem, "rss", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
                 if(cg->memory.detailed_has_swap)
-                    rrddim_add(cg->st_mem, "swap", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
-                rrddim_add(cg->st_mem, "rss_huge", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
-                rrddim_add(cg->st_mem, "mapped_file", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+                    rrddim_add(cg->st_mem, "swap", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
+                rrddim_add(cg->st_mem, "rss_huge", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
+                rrddim_add(cg->st_mem, "mapped_file", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
             }
             else
                 rrdset_next(cg->st_mem);
@@ -1848,15 +1931,18 @@ void update_cgroup_charts(int update_every) {
             rrdset_done(cg->st_mem);
 
             if(unlikely(!cg->st_writeback)) {
-                cg->st_writeback = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "writeback");
+                cg->st_writeback = rrdset_find_bytype_localhost(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX)
+                                                                , "writeback");
                 if(likely(!cg->st_writeback)) {
                     snprintfz(title, CHART_TITLE_MAX, "Writeback Memory for cgroup %s", cg->chart_title);
-                    cg->st_writeback = rrdset_create(type, "writeback", NULL, "mem", "cgroup.writeback", title, "MB", CHART_PRIORITY_CONTAINERS + 300, update_every, RRDSET_TYPE_AREA);
+                    cg->st_writeback = rrdset_create_localhost(type, "writeback", NULL, "mem", "cgroup.writeback", title
+                                                               , "MB", CHART_PRIORITY_CONTAINERS + 300, update_every
+                                                               , RRDSET_TYPE_AREA);
                 }
 
                 if(cg->memory.detailed_has_dirty)
-                    rrddim_add(cg->st_writeback, "dirty", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
-                rrddim_add(cg->st_writeback, "writeback", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+                    rrddim_add(cg->st_writeback, "dirty", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
+                rrddim_add(cg->st_writeback, "writeback", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
             }
             else
                 rrdset_next(cg->st_writeback);
@@ -1867,13 +1953,16 @@ void update_cgroup_charts(int update_every) {
             rrdset_done(cg->st_writeback);
 
             if(unlikely(!cg->st_mem_activity)) {
-                cg->st_mem_activity = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem_activity");
+                cg->st_mem_activity = rrdset_find_bytype_localhost(
+                        cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem_activity");
                 if(likely(!cg->st_mem_activity)) {
                     snprintfz(title, CHART_TITLE_MAX, "Memory Activity for cgroup %s", cg->chart_title);
-                    cg->st_mem_activity = rrdset_create(type, "mem_activity", NULL, "mem", "cgroup.mem_activity", title, "MB/s", CHART_PRIORITY_CONTAINERS + 400, update_every, RRDSET_TYPE_LINE);
+                    cg->st_mem_activity = rrdset_create_localhost(type, "mem_activity", NULL, "mem"
+                                                                  , "cgroup.mem_activity", title, "MB/s",
+                            CHART_PRIORITY_CONTAINERS + 400, update_every, RRDSET_TYPE_LINE);
                 }
-                rrddim_add(cg->st_mem_activity, "pgpgin", "in", system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL);
-                rrddim_add(cg->st_mem_activity, "pgpgout", "out", -system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL);
+                rrddim_add(cg->st_mem_activity, "pgpgin", "in", system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
+                rrddim_add(cg->st_mem_activity, "pgpgout", "out", -system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
             }
             else
                 rrdset_next(cg->st_mem_activity);
@@ -1883,13 +1972,16 @@ void update_cgroup_charts(int update_every) {
             rrdset_done(cg->st_mem_activity);
 
             if(unlikely(!cg->st_pgfaults)) {
-                cg->st_pgfaults = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "pgfaults");
+                cg->st_pgfaults = rrdset_find_bytype_localhost(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX)
+                                                               , "pgfaults");
                 if(likely(!cg->st_pgfaults)) {
                     snprintfz(title, CHART_TITLE_MAX, "Memory Page Faults for cgroup %s", cg->chart_title);
-                    cg->st_pgfaults = rrdset_create(type, "pgfaults", NULL, "mem", "cgroup.pgfaults", title, "MB/s", CHART_PRIORITY_CONTAINERS + 500, update_every, RRDSET_TYPE_LINE);
+                    cg->st_pgfaults = rrdset_create_localhost(type, "pgfaults", NULL, "mem", "cgroup.pgfaults", title
+                                                              , "MB/s", CHART_PRIORITY_CONTAINERS + 500, update_every
+                                                              , RRDSET_TYPE_LINE);
                 }
-                rrddim_add(cg->st_pgfaults, "pgfault", NULL, system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL);
-                rrddim_add(cg->st_pgfaults, "pgmajfault", "swap", -system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL);
+                rrddim_add(cg->st_pgfaults, "pgfault", NULL, system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
+                rrddim_add(cg->st_pgfaults, "pgmajfault", "swap", -system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL);
             }
             else
                 rrdset_next(cg->st_pgfaults);
@@ -1899,15 +1991,18 @@ void update_cgroup_charts(int update_every) {
             rrdset_done(cg->st_pgfaults);
         }
 
-        if(likely(cg->memory.updated_usage_in_bytes && cg->memory.enabled_usage_in_bytes == CONFIG_ONDEMAND_YES)) {
+        if(likely(cg->memory.updated_usage_in_bytes && cg->memory.enabled_usage_in_bytes == CONFIG_BOOLEAN_YES)) {
             if(unlikely(!cg->st_mem_usage)) {
-                cg->st_mem_usage = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem_usage");
+                cg->st_mem_usage = rrdset_find_bytype_localhost(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX)
+                                                                , "mem_usage");
                 if(likely(!cg->st_mem_usage)) {
                     snprintfz(title, CHART_TITLE_MAX, "Used Memory %sfor cgroup %s", (cgroup_used_memory_without_cache && cg->memory.updated_detailed)?"without Cache ":"", cg->chart_title);
-                    cg->st_mem_usage = rrdset_create(type, "mem_usage", NULL, "mem", "cgroup.mem_usage", title, "MB", CHART_PRIORITY_CONTAINERS + 200, update_every, RRDSET_TYPE_STACKED);
+                    cg->st_mem_usage = rrdset_create_localhost(type, "mem_usage", NULL, "mem", "cgroup.mem_usage", title
+                                                               , "MB", CHART_PRIORITY_CONTAINERS + 200, update_every
+                                                               , RRDSET_TYPE_STACKED);
                 }
-                rrddim_add(cg->st_mem_usage, "ram", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
-                rrddim_add(cg->st_mem_usage, "swap", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+                rrddim_add(cg->st_mem_usage, "ram", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
+                rrddim_add(cg->st_mem_usage, "swap", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
             }
             else
                 rrdset_next(cg->st_mem_usage);
@@ -1917,14 +2012,17 @@ void update_cgroup_charts(int update_every) {
             rrdset_done(cg->st_mem_usage);
         }
 
-        if(likely(cg->memory.updated_failcnt && cg->memory.enabled_failcnt == CONFIG_ONDEMAND_YES)) {
+        if(likely(cg->memory.updated_failcnt && cg->memory.enabled_failcnt == CONFIG_BOOLEAN_YES)) {
             if(unlikely(!cg->st_mem_failcnt)) {
-                cg->st_mem_failcnt = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem_failcnt");
+                cg->st_mem_failcnt = rrdset_find_bytype_localhost(
+                        cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem_failcnt");
                 if(likely(!cg->st_mem_failcnt)) {
                     snprintfz(title, CHART_TITLE_MAX, "Memory Limit Failures for cgroup %s", cg->chart_title);
-                    cg->st_mem_failcnt = rrdset_create(type, "mem_failcnt", NULL, "mem", "cgroup.mem_failcnt", title, "count", CHART_PRIORITY_CONTAINERS + 250, update_every, RRDSET_TYPE_LINE);
+                    cg->st_mem_failcnt = rrdset_create_localhost(type, "mem_failcnt", NULL, "mem", "cgroup.mem_failcnt"
+                                                                 , title, "count", CHART_PRIORITY_CONTAINERS + 250
+                                                                 , update_every, RRDSET_TYPE_LINE);
                 }
-                rrddim_add(cg->st_mem_failcnt, "failures", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(cg->st_mem_failcnt, "failures", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
             }
             else
                 rrdset_next(cg->st_mem_failcnt);
@@ -1933,15 +2031,16 @@ void update_cgroup_charts(int update_every) {
             rrdset_done(cg->st_mem_failcnt);
         }
 
-        if(likely(cg->io_service_bytes.updated && cg->io_service_bytes.enabled == CONFIG_ONDEMAND_YES)) {
+        if(likely(cg->io_service_bytes.updated && cg->io_service_bytes.enabled == CONFIG_BOOLEAN_YES)) {
             if(unlikely(!cg->st_io)) {
-                cg->st_io = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "io");
+                cg->st_io = rrdset_find_bytype_localhost(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "io");
                 if(likely(!cg->st_io)) {
                     snprintfz(title, CHART_TITLE_MAX, "I/O Bandwidth (all disks) for cgroup %s", cg->chart_title);
-                    cg->st_io = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", CHART_PRIORITY_CONTAINERS + 1200, update_every, RRDSET_TYPE_AREA);
+                    cg->st_io = rrdset_create_localhost(type, "io", NULL, "disk", "cgroup.io", title, "KB/s",
+                            CHART_PRIORITY_CONTAINERS + 1200, update_every, RRDSET_TYPE_AREA);
                 }
-                rrddim_add(cg->st_io, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
-                rrddim_add(cg->st_io, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
+                rrddim_add(cg->st_io, "read", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
+                rrddim_add(cg->st_io, "write", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL);
             }
             else
                 rrdset_next(cg->st_io);
@@ -1951,15 +2050,18 @@ void update_cgroup_charts(int update_every) {
             rrdset_done(cg->st_io);
         }
 
-        if(likely(cg->io_serviced.updated && cg->io_serviced.enabled == CONFIG_ONDEMAND_YES)) {
+        if(likely(cg->io_serviced.updated && cg->io_serviced.enabled == CONFIG_BOOLEAN_YES)) {
             if(unlikely(!cg->st_serviced_ops)) {
-                cg->st_serviced_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "serviced_ops");
+                cg->st_serviced_ops = rrdset_find_bytype_localhost(
+                        cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "serviced_ops");
                 if(likely(!cg->st_serviced_ops)) {
                     snprintfz(title, CHART_TITLE_MAX, "Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title);
-                    cg->st_serviced_ops = rrdset_create(type, "serviced_ops", NULL, "disk", "cgroup.serviced_ops", title, "operations/s", CHART_PRIORITY_CONTAINERS + 1200, update_every, RRDSET_TYPE_LINE);
+                    cg->st_serviced_ops = rrdset_create_localhost(type, "serviced_ops", NULL, "disk"
+                                                                  , "cgroup.serviced_ops", title, "operations/s",
+                            CHART_PRIORITY_CONTAINERS + 1200, update_every, RRDSET_TYPE_LINE);
                 }
-                rrddim_add(cg->st_serviced_ops, "read", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                rrddim_add(cg->st_serviced_ops, "write", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(cg->st_serviced_ops, "read", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                rrddim_add(cg->st_serviced_ops, "write", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
             }
             else
                 rrdset_next(cg->st_serviced_ops);
@@ -1969,15 +2071,18 @@ void update_cgroup_charts(int update_every) {
             rrdset_done(cg->st_serviced_ops);
         }
 
-        if(likely(cg->throttle_io_service_bytes.updated && cg->throttle_io_service_bytes.enabled == CONFIG_ONDEMAND_YES)) {
+        if(likely(cg->throttle_io_service_bytes.updated && cg->throttle_io_service_bytes.enabled == CONFIG_BOOLEAN_YES)) {
             if(unlikely(!cg->st_throttle_io)) {
-                cg->st_throttle_io = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "throttle_io");
+                cg->st_throttle_io = rrdset_find_bytype_localhost(
+                        cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "throttle_io");
                 if(likely(!cg->st_throttle_io)) {
                     snprintfz(title, CHART_TITLE_MAX, "Throttle I/O Bandwidth (all disks) for cgroup %s", cg->chart_title);
-                    cg->st_throttle_io = rrdset_create(type, "throttle_io", NULL, "disk", "cgroup.throttle_io", title, "KB/s", CHART_PRIORITY_CONTAINERS + 1200, update_every, RRDSET_TYPE_AREA);
+                    cg->st_throttle_io = rrdset_create_localhost(type, "throttle_io", NULL, "disk", "cgroup.throttle_io"
+                                                                 , title, "KB/s", CHART_PRIORITY_CONTAINERS + 1200
+                                                                 , update_every, RRDSET_TYPE_AREA);
                 }
-                rrddim_add(cg->st_throttle_io, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
-                rrddim_add(cg->st_throttle_io, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
+                rrddim_add(cg->st_throttle_io, "read", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
+                rrddim_add(cg->st_throttle_io, "write", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL);
             }
             else
                 rrdset_next(cg->st_throttle_io);
@@ -1987,15 +2092,20 @@ void update_cgroup_charts(int update_every) {
             rrdset_done(cg->st_throttle_io);
         }
 
-        if(likely(cg->throttle_io_serviced.updated && cg->throttle_io_serviced.enabled == CONFIG_ONDEMAND_YES)) {
+        if(likely(cg->throttle_io_serviced.updated && cg->throttle_io_serviced.enabled == CONFIG_BOOLEAN_YES)) {
             if(unlikely(!cg->st_throttle_serviced_ops)) {
-                cg->st_throttle_serviced_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "throttle_serviced_ops");
+                cg->st_throttle_serviced_ops = rrdset_find_bytype_localhost(
+                        cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "throttle_serviced_ops");
                 if(likely(!cg->st_throttle_serviced_ops)) {
                     snprintfz(title, CHART_TITLE_MAX, "Throttle Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title);
-                    cg->st_throttle_serviced_ops = rrdset_create(type, "throttle_serviced_ops", NULL, "disk", "cgroup.throttle_serviced_ops", title, "operations/s", CHART_PRIORITY_CONTAINERS + 1200, update_every, RRDSET_TYPE_LINE);
+                    cg->st_throttle_serviced_ops = rrdset_create_localhost(type, "throttle_serviced_ops", NULL, "disk"
+                                                                           , "cgroup.throttle_serviced_ops", title
+                                                                           , "operations/s", CHART_PRIORITY_CONTAINERS +
+                                                                                             1200, update_every
+                                                                           , RRDSET_TYPE_LINE);
                 }
-                rrddim_add(cg->st_throttle_serviced_ops, "read", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                rrddim_add(cg->st_throttle_serviced_ops, "write", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(cg->st_throttle_serviced_ops, "read", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
+                rrddim_add(cg->st_throttle_serviced_ops, "write", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
             }
             else
                 rrdset_next(cg->st_throttle_serviced_ops);
@@ -2005,15 +2115,18 @@ void update_cgroup_charts(int update_every) {
             rrdset_done(cg->st_throttle_serviced_ops);
         }
 
-        if(likely(cg->io_queued.updated && cg->io_queued.enabled == CONFIG_ONDEMAND_YES)) {
+        if(likely(cg->io_queued.updated && cg->io_queued.enabled == CONFIG_BOOLEAN_YES)) {
             if(unlikely(!cg->st_queued_ops)) {
-                cg->st_queued_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "queued_ops");
+                cg->st_queued_ops = rrdset_find_bytype_localhost(
+                        cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "queued_ops");
                 if(likely(!cg->st_queued_ops)) {
                     snprintfz(title, CHART_TITLE_MAX, "Queued I/O Operations (all disks) for cgroup %s", cg->chart_title);
-                    cg->st_queued_ops = rrdset_create(type, "queued_ops", NULL, "disk", "cgroup.queued_ops", title, "operations", CHART_PRIORITY_CONTAINERS + 2000, update_every, RRDSET_TYPE_LINE);
+                    cg->st_queued_ops = rrdset_create_localhost(type, "queued_ops", NULL, "disk", "cgroup.queued_ops"
+                                                                , title, "operations", CHART_PRIORITY_CONTAINERS + 2000
+                                                                , update_every, RRDSET_TYPE_LINE);
                 }
-                rrddim_add(cg->st_queued_ops, "read", NULL, 1, 1, RRDDIM_ABSOLUTE);
-                rrddim_add(cg->st_queued_ops, "write", NULL, -1, 1, RRDDIM_ABSOLUTE);
+                rrddim_add(cg->st_queued_ops, "read", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+                rrddim_add(cg->st_queued_ops, "write", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE);
             }
             else
                 rrdset_next(cg->st_queued_ops);
@@ -2023,15 +2136,19 @@ void update_cgroup_charts(int update_every) {
             rrdset_done(cg->st_queued_ops);
         }
 
-        if(likely(cg->io_merged.updated && cg->io_merged.enabled == CONFIG_ONDEMAND_YES)) {
+        if(likely(cg->io_merged.updated && cg->io_merged.enabled == CONFIG_BOOLEAN_YES)) {
             if(unlikely(!cg->st_merged_ops)) {
-                cg->st_merged_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "merged_ops");
+                cg->st_merged_ops = rrdset_find_bytype_localhost(
+                        cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "merged_ops");
                 if(likely(!cg->st_merged_ops)) {
                     snprintfz(title, CHART_TITLE_MAX, "Merged I/O Operations (all disks) for cgroup %s", cg->chart_title);
-                    cg->st_merged_ops = rrdset_create(type, "merged_ops", NULL, "disk", "cgroup.merged_ops", title, "operations/s", CHART_PRIORITY_CONTAINERS + 2100, update_every, RRDSET_TYPE_LINE);
+                    cg->st_merged_ops = rrdset_create_localhost(type, "merged_ops", NULL, "disk", "cgroup.merged_ops"
+                                                                , title, "operations/s", CHART_PRIORITY_CONTAINERS +
+                                                                                         2100, update_every
+                                                                , RRDSET_TYPE_LINE);
                 }
-                rrddim_add(cg->st_merged_ops, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
-                rrddim_add(cg->st_merged_ops, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
+                rrddim_add(cg->st_merged_ops, "read", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
+                rrddim_add(cg->st_merged_ops, "write", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL);
             }
             else
                 rrdset_next(cg->st_merged_ops);
@@ -2110,12 +2227,14 @@ void *cgroups_main(void *ptr) {
             getrusage(RUSAGE_THREAD, &thread);
 
             if(unlikely(!stcpu_thread)) {
-                stcpu_thread = rrdset_find("netdata.plugin_cgroups_cpu");
+                stcpu_thread = rrdset_find_localhost("netdata.plugin_cgroups_cpu");
                 if(unlikely(!stcpu_thread))
-                    stcpu_thread = rrdset_create("netdata", "plugin_cgroups_cpu", NULL, "cgroups", NULL, "NetData CGroups Plugin CPU usage", "milliseconds/s", 132000, cgroup_update_every, RRDSET_TYPE_STACKED);
+                    stcpu_thread = rrdset_create_localhost("netdata", "plugin_cgroups_cpu", NULL, "cgroups", NULL
+                                                           , "NetData CGroups Plugin CPU usage", "milliseconds/s"
+                                                           , 132000, cgroup_update_every, RRDSET_TYPE_STACKED);
 
-                rrddim_add(stcpu_thread, "user",  NULL,  1, 1000, RRDDIM_INCREMENTAL);
-                rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);
+                rrddim_add(stcpu_thread, "user",  NULL,  1, 1000, RRD_ALGORITHM_INCREMENTAL);
+                rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL);
             }
             else
                 rrdset_next(stcpu_thread);
index 4998b0108321e456c8159888359894be4f53891a..76d808538b1cdcdd1ce9bdd759ebaf77b14aebfe 100644 (file)
@@ -90,15 +90,16 @@ int do_sys_kernel_mm_ksm(int update_every, usec_t dt) {
 
     // --------------------------------------------------------------------
 
-    st = rrdset_find("mem.ksm");
+    st = rrdset_find_localhost("mem.ksm");
     if(!st) {
-        st = rrdset_create("mem", "ksm", NULL, "ksm", NULL, "Kernel Same Page Merging", "MB", 5000, update_every, RRDSET_TYPE_AREA);
-
-        rrddim_add(st, "shared", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
-        rrddim_add(st, "unshared", NULL, -1, 1024 * 1024, RRDDIM_ABSOLUTE);
-        rrddim_add(st, "sharing", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
-        rrddim_add(st, "volatile", NULL, -1, 1024 * 1024, RRDDIM_ABSOLUTE);
-        rrddim_add(st, "to_scan", "to scan", -1, 1024 * 1024, RRDDIM_ABSOLUTE);
+        st = rrdset_create_localhost("mem", "ksm", NULL, "ksm", NULL, "Kernel Same Page Merging", "MB", 5000
+                                     , update_every, RRDSET_TYPE_AREA);
+
+        rrddim_add(st, "shared", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
+        rrddim_add(st, "unshared", NULL, -1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
+        rrddim_add(st, "sharing", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
+        rrddim_add(st, "volatile", NULL, -1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
+        rrddim_add(st, "to_scan", "to scan", -1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
     }
     else rrdset_next(st);
 
@@ -109,12 +110,13 @@ int do_sys_kernel_mm_ksm(int update_every, usec_t dt) {
     rrddim_set(st, "to_scan", pages_to_scan * page_size);
     rrdset_done(st);
 
-    st = rrdset_find("mem.ksm_savings");
+    st = rrdset_find_localhost("mem.ksm_savings");
     if(!st) {
-        st = rrdset_create("mem", "ksm_savings", NULL, "ksm", NULL, "Kernel Same Page Merging Savings", "MB", 5001, update_every, RRDSET_TYPE_AREA);
+        st = rrdset_create_localhost("mem", "ksm_savings", NULL, "ksm", NULL, "Kernel Same Page Merging Savings", "MB"
+                                     , 5001, update_every, RRDSET_TYPE_AREA);
 
-        rrddim_add(st, "savings", NULL, -1, 1024 * 1024, RRDDIM_ABSOLUTE);
-        rrddim_add(st, "offered", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+        rrddim_add(st, "savings", NULL, -1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
+        rrddim_add(st, "offered", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE);
     }
     else rrdset_next(st);
 
@@ -122,11 +124,12 @@ int do_sys_kernel_mm_ksm(int update_every, usec_t dt) {
     rrddim_set(st, "offered", offered * page_size);
     rrdset_done(st);
 
-    st = rrdset_find("mem.ksm_ratios");
+    st = rrdset_find_localhost("mem.ksm_ratios");
     if(!st) {
-        st = rrdset_create("mem", "ksm_ratios", NULL, "ksm", NULL, "Kernel Same Page Merging Effectiveness", "percentage", 5002, update_every, RRDSET_TYPE_LINE);
+        st = rrdset_create_localhost("mem", "ksm_ratios", NULL, "ksm", NULL, "Kernel Same Page Merging Effectiveness"
+                                     , "percentage", 5002, update_every, RRDSET_TYPE_LINE);
 
-        rrddim_add(st, "savings", NULL, 1, 10000, RRDDIM_ABSOLUTE);
+        rrddim_add(st, "savings", NULL, 1, 10000, RRD_ALGORITHM_ABSOLUTE);
     }
     else rrdset_next(st);
 
index 4e2f10c0a44b156a174b4cd3bd522463e0328457..0866d215c8d3d06eb126be015b17175edfa86c83 100644 (file)
@@ -282,7 +282,7 @@ struct test test1 = {
         1,                  // update_every
         1,                  // multiplier
         1,                  // divisor
-        RRDDIM_ABSOLUTE,    // algorithm
+        RRD_ALGORITHM_ABSOLUTE,    // algorithm
         10,                 // feed entries
         9,                  // result entries
         test1_feed,         // feed
@@ -318,7 +318,7 @@ struct test test2 = {
         1,                  // update_every
         1,                  // multiplier
         1,                  // divisor
-        RRDDIM_ABSOLUTE,    // algorithm
+        RRD_ALGORITHM_ABSOLUTE,    // algorithm
         10,                 // feed entries
         9,                  // result entries
         test2_feed,         // feed
@@ -353,7 +353,7 @@ struct test test3 = {
         1,                  // update_every
         1,                  // multiplier
         1,                  // divisor
-        RRDDIM_INCREMENTAL, // algorithm
+        RRD_ALGORITHM_INCREMENTAL, // algorithm
         10,                 // feed entries
         9,                  // result entries
         test3_feed,         // feed
@@ -388,7 +388,7 @@ struct test test4 = {
         1,                  // update_every
         1,                  // multiplier
         1,                  // divisor
-        RRDDIM_INCREMENTAL, // algorithm
+        RRD_ALGORITHM_INCREMENTAL, // algorithm
         10,                 // feed entries
         9,                  // result entries
         test4_feed,         // feed
@@ -423,7 +423,7 @@ struct test test5 = {
         1,                  // update_every
         1,                  // multiplier
         1,                  // divisor
-        RRDDIM_INCREMENTAL, // algorithm
+        RRD_ALGORITHM_INCREMENTAL, // algorithm
         10,                 // feed entries
         9,                  // result entries
         test5_feed,         // feed
@@ -464,7 +464,7 @@ struct test test6 = {
         1,                  // update_every
         1,                  // multiplier
         1,                  // divisor
-        RRDDIM_INCREMENTAL, // algorithm
+        RRD_ALGORITHM_INCREMENTAL, // algorithm
         16,                 // feed entries
         4,                  // result entries
         test6_feed,         // feed
@@ -499,7 +499,7 @@ struct test test7 = {
         1,                  // update_every
         1,                  // multiplier
         1,                  // divisor
-        RRDDIM_INCREMENTAL, // algorithm
+        RRD_ALGORITHM_INCREMENTAL, // algorithm
         10,                 // feed entries
         18,                 // result entries
         test7_feed,         // feed
@@ -530,7 +530,7 @@ struct test test8 = {
         1,                  // update_every
         1,                  // multiplier
         1,                  // divisor
-        RRDDIM_ABSOLUTE,    // algorithm
+        RRD_ALGORITHM_ABSOLUTE,    // algorithm
         6,                  // feed entries
         10,                 // result entries
         test8_feed,         // feed
@@ -571,7 +571,7 @@ struct test test9 = {
         1,                  // update_every
         1,                  // multiplier
         1,                  // divisor
-        RRDDIM_ABSOLUTE,    // algorithm
+        RRD_ALGORITHM_ABSOLUTE,    // algorithm
         16,                 // feed entries
         4,                  // result entries
         test9_feed,         // feed
@@ -606,7 +606,7 @@ struct test test10 = {
         1,                  // update_every
         1,                  // multiplier
         1,                  // divisor
-        RRDDIM_INCREMENTAL, // algorithm
+        RRD_ALGORITHM_INCREMENTAL, // algorithm
         10,                 // feed entries
         7,                  // result entries
         test10_feed,        // feed
@@ -649,7 +649,7 @@ struct test test11 = {
         1,                  // update_every
         1,                  // multiplier
         1,                  // divisor
-        RRDDIM_PCENT_OVER_DIFF_TOTAL,   // algorithm
+        RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL,   // algorithm
         10,                 // feed entries
         9,                  // result entries
         test11_feed,        // feed
@@ -692,7 +692,7 @@ struct test test12 = {
         1,                  // update_every
         1,                  // multiplier
         1,                  // divisor
-        RRDDIM_PCENT_OVER_DIFF_TOTAL,   // algorithm
+        RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL,   // algorithm
         10,                 // feed entries
         9,                  // result entries
         test12_feed,        // feed
@@ -727,7 +727,7 @@ struct test test13 = {
         1,                  // update_every
         1,                  // multiplier
         1,                  // divisor
-        RRDDIM_PCENT_OVER_DIFF_TOTAL,   // algorithm
+        RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL,   // algorithm
         10,                 // feed entries
         7,                  // result entries
         test13_feed,        // feed
@@ -762,7 +762,7 @@ struct test test14 = {
         30,                 // update_every
         8,                  // multiplier
         1000000000,         // divisor
-        RRDDIM_INCREMENTAL, // algorithm
+        RRD_ALGORITHM_INCREMENTAL, // algorithm
         10,                 // feed entries
         8,                  // result entries
         test14_feed,        // feed
@@ -794,7 +794,7 @@ struct test test14b = {
         30,                 // update_every
         1,                  // multiplier
         1,                  // divisor
-        RRDDIM_INCREMENTAL, // algorithm
+        RRD_ALGORITHM_INCREMENTAL, // algorithm
         10,                 // feed entries
         8,                  // result entries
         test14b_feed,        // feed
@@ -826,7 +826,7 @@ struct test test14c = {
         30,                 // update_every
         1,                  // multiplier
         1,                  // divisor
-        RRDDIM_INCREMENTAL, // algorithm
+        RRD_ALGORITHM_INCREMENTAL, // algorithm
         10,                 // feed entries
         9,                  // result entries
         test14c_feed,        // feed
@@ -869,7 +869,7 @@ struct test test15 = {
         1,                  // update_every
         8,                  // multiplier
         1024,               // divisor
-        RRDDIM_INCREMENTAL, // algorithm
+        RRD_ALGORITHM_INCREMENTAL, // algorithm
         10,                 // feed entries
         9,                  // result entries
         test15_feed,        // feed
@@ -884,21 +884,22 @@ int run_test(struct test *test)
 {
     fprintf(stderr, "\nRunning test '%s':\n%s\n", test->name, test->description);
 
-    rrd_memory_mode = RRD_MEMORY_MODE_RAM;
-    rrd_update_every = test->update_every;
+    default_rrd_memory_mode = RRD_MEMORY_MODE_RAM;
+    default_rrd_update_every = test->update_every;
 
     char name[101];
     snprintfz(name, 100, "unittest-%s", test->name);
 
     // create the chart
-    RRDSET *st = rrdset_create("netdata", name, name, "netdata", NULL, "Unit Testing", "a value", 1, test->update_every, RRDSET_TYPE_LINE);
+    RRDSET *st = rrdset_create_localhost("netdata", name, name, "netdata", NULL, "Unit Testing", "a value", 1
+                                         , test->update_every, RRDSET_TYPE_LINE);
     RRDDIM *rd = rrddim_add(st, "dim1", NULL, test->multiplier, test->divisor, test->algorithm);
     
     RRDDIM *rd2 = NULL;
     if(test->feed2)
         rd2 = rrddim_add(st, "dim2", NULL, test->multiplier, test->divisor, test->algorithm);
 
-    st->debug = 1;
+    rrdset_flag_set(st, RRDSET_FLAG_DEBUG);
 
     // feed it with the test data
     time_t time_now = 0, time_start = now_realtime_sec();
@@ -977,15 +978,16 @@ int run_test(struct test *test)
 
 static int test_variable_renames(void) {
     fprintf(stderr, "Creating chart\n");
-    RRDSET *st = rrdset_create("chart", "ID", NULL, "family", "context", "Unit Testing", "a value", 1, 1, RRDSET_TYPE_LINE);
+    RRDSET *st = rrdset_create_localhost("chart", "ID", NULL, "family", "context", "Unit Testing", "a value", 1, 1
+                                         , RRDSET_TYPE_LINE);
     fprintf(stderr, "Created chart with id '%s', name '%s'\n", st->id, st->name);
 
     fprintf(stderr, "Creating dimension DIM1\n");
-    RRDDIM *rd1 = rrddim_add(st, "DIM1", NULL, 1, 1, RRDDIM_INCREMENTAL);
+    RRDDIM *rd1 = rrddim_add(st, "DIM1", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
     fprintf(stderr, "Created dimension with id '%s', name '%s'\n", rd1->id, rd1->name);
 
     fprintf(stderr, "Creating dimension DIM2\n");
-    RRDDIM *rd2 = rrddim_add(st, "DIM2", NULL, 1, 1, RRDDIM_INCREMENTAL);
+    RRDDIM *rd2 = rrddim_add(st, "DIM2", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
     fprintf(stderr, "Created dimension with id '%s', name '%s'\n", rd2->id, rd2->name);
 
     fprintf(stderr, "Renaming chart to CHARTNAME1\n");
@@ -1089,26 +1091,27 @@ int unit_test(long delay, long shift)
     snprintfz(name, 100, "unittest-%d-%ld-%ld", repeat, delay, shift);
 
     //debug_flags = 0xffffffff;
-    rrd_memory_mode = RRD_MEMORY_MODE_RAM;
-    rrd_update_every = 1;
+    default_rrd_memory_mode = RRD_MEMORY_MODE_RAM;
+    default_rrd_update_every = 1;
 
     int do_abs = 1;
     int do_inc = 1;
     int do_abst = 0;
     int do_absi = 0;
 
-    RRDSET *st = rrdset_create("netdata", name, name, "netdata", NULL, "Unit Testing", "a value", 1, 1, RRDSET_TYPE_LINE);
-    st->debug = 1;
+    RRDSET *st = rrdset_create_localhost("netdata", name, name, "netdata", NULL, "Unit Testing", "a value", 1, 1
+                                         , RRDSET_TYPE_LINE);
+    rrdset_flag_set(st, RRDSET_FLAG_DEBUG);
 
     RRDDIM *rdabs = NULL;
     RRDDIM *rdinc = NULL;
     RRDDIM *rdabst = NULL;
     RRDDIM *rdabsi = NULL;
 
-    if(do_abs) rdabs = rrddim_add(st, "absolute", "absolute", 1, 1, RRDDIM_ABSOLUTE);
-    if(do_inc) rdinc = rrddim_add(st, "incremental", "incremental", 1, 1, RRDDIM_INCREMENTAL);
-    if(do_abst) rdabst = rrddim_add(st, "percentage-of-absolute-row", "percentage-of-absolute-row", 1, 1, RRDDIM_PCENT_OVER_ROW_TOTAL);
-    if(do_absi) rdabsi = rrddim_add(st, "percentage-of-incremental-row", "percentage-of-incremental-row", 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+    if(do_abs) rdabs = rrddim_add(st, "absolute", "absolute", 1, 1, RRD_ALGORITHM_ABSOLUTE);
+    if(do_inc) rdinc = rrddim_add(st, "incremental", "incremental", 1, 1, RRD_ALGORITHM_INCREMENTAL);
+    if(do_abst) rdabst = rrddim_add(st, "percentage-of-absolute-row", "percentage-of-absolute-row", 1, 1, RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL);
+    if(do_absi) rdabsi = rrddim_add(st, "percentage-of-incremental-row", "percentage-of-incremental-row", 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL);
 
     long increment = 1000;
     collected_number i = 0;
diff --git a/src/web_api_old.c b/src/web_api_old.c
new file mode 100644 (file)
index 0000000..127536e
--- /dev/null
@@ -0,0 +1,231 @@
+#include "common.h"
+
+int web_client_api_old_data_request(RRDHOST *host, struct web_client *w, char *url, int datasource_type) {
+    if(!url || !*url) {
+        buffer_flush(w->response.data);
+        buffer_sprintf(w->response.data, "Incomplete request.");
+        return 400;
+    }
+
+    RRDSET *st = NULL;
+
+    char *args = strchr(url, '?');
+    if(args) {
+        *args='\0';
+        args = &args[1];
+    }
+
+    // get the name of the data to show
+    char *tok = mystrsep(&url, "/");
+    if(!tok) tok = "";
+
+    // do we have such a data set?
+    if(*tok) {
+        debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
+        st = rrdset_find_byname(host, tok);
+        if(!st) st = rrdset_find(host, tok);
+    }
+
+    if(!st) {
+        // we don't have it
+        // try to send a file with that name
+        buffer_flush(w->response.data);
+        return(mysendfile(w, tok));
+    }
+
+    // we have it
+    debug(D_WEB_CLIENT, "%llu: Found RRD data with name '%s'.", w->id, tok);
+
+    // how many entries does the client want?
+    int lines = (int)st->entries;
+    int group_count = 1;
+    time_t after = 0, before = 0;
+    int group_method = GROUP_AVERAGE;
+    int nonzero = 0;
+
+    if(url) {
+        // parse the lines required
+        tok = mystrsep(&url, "/");
+        if(tok) lines = str2i(tok);
+        if(lines < 1) lines = 1;
+    }
+    if(url) {
+        // parse the group count required
+        tok = mystrsep(&url, "/");
+        if(tok && *tok) group_count = str2i(tok);
+        if(group_count < 1) group_count = 1;
+        //if(group_count > save_history / 20) group_count = save_history / 20;
+    }
+    if(url) {
+        // parse the grouping method required
+        tok = mystrsep(&url, "/");
+        if(tok && *tok) {
+            if(strcmp(tok, "max") == 0) group_method = GROUP_MAX;
+            else if(strcmp(tok, "average") == 0) group_method = GROUP_AVERAGE;
+            else if(strcmp(tok, "sum") == 0) group_method = GROUP_SUM;
+            else debug(D_WEB_CLIENT, "%llu: Unknown group method '%s'", w->id, tok);
+        }
+    }
+    if(url) {
+        // parse after time
+        tok = mystrsep(&url, "/");
+        if(tok && *tok) after = str2ul(tok);
+        if(after < 0) after = 0;
+    }
+    if(url) {
+        // parse before time
+        tok = mystrsep(&url, "/");
+        if(tok && *tok) before = str2ul(tok);
+        if(before < 0) before = 0;
+    }
+    if(url) {
+        // parse nonzero
+        tok = mystrsep(&url, "/");
+        if(tok && *tok && strcmp(tok, "nonzero") == 0) nonzero = 1;
+    }
+
+    w->response.data->contenttype = CT_APPLICATION_JSON;
+    buffer_flush(w->response.data);
+
+    char *google_version = "0.6";
+    char *google_reqId = "0";
+    char *google_sig = "0";
+    char *google_out = "json";
+    char *google_responseHandler = "google.visualization.Query.setResponse";
+    char *google_outFileName = NULL;
+    time_t last_timestamp_in_data = 0;
+    if(datasource_type == DATASOURCE_DATATABLE_JSON || datasource_type == DATASOURCE_DATATABLE_JSONP) {
+
+        w->response.data->contenttype = CT_APPLICATION_X_JAVASCRIPT;
+
+        while(args) {
+            tok = mystrsep(&args, "&");
+            if(tok && *tok) {
+                char *name = mystrsep(&tok, "=");
+                if(name && *name && strcmp(name, "tqx") == 0) {
+                    char *key = mystrsep(&tok, ":");
+                    char *value = mystrsep(&tok, ";");
+                    if(key && value && *key && *value) {
+                        if(strcmp(key, "version") == 0)
+                            google_version = value;
+
+                        else if(strcmp(key, "reqId") == 0)
+                            google_reqId = value;
+
+                        else if(strcmp(key, "sig") == 0)
+                            google_sig = value;
+
+                        else if(strcmp(key, "out") == 0)
+                            google_out = value;
+
+                        else if(strcmp(key, "responseHandler") == 0)
+                            google_responseHandler = value;
+
+                        else if(strcmp(key, "outFileName") == 0)
+                            google_outFileName = value;
+                    }
+                }
+            }
+        }
+
+        debug(D_WEB_CLIENT_ACCESS, "%llu: GOOGLE JSONP: version = '%s', reqId = '%s', sig = '%s', out = '%s', responseHandler = '%s', outFileName = '%s'",
+                w->id, google_version, google_reqId, google_sig, google_out, google_responseHandler, google_outFileName
+        );
+
+        if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
+            last_timestamp_in_data = strtoul(google_sig, NULL, 0);
+
+            // check the client wants json
+            if(strcmp(google_out, "json") != 0) {
+                buffer_sprintf(w->response.data,
+                        "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'invalid_query',message:'output format is not supported',detailed_message:'the format %s requested is not supported by netdata.'}]});",
+                        google_responseHandler, google_version, google_reqId, google_out);
+                return 200;
+            }
+        }
+    }
+
+    if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
+        buffer_sprintf(w->response.data,
+                "%s({version:'%s',reqId:'%s',status:'ok',sig:'%ld',table:",
+                google_responseHandler, google_version, google_reqId, st->last_updated.tv_sec);
+    }
+
+    debug(D_WEB_CLIENT_ACCESS, "%llu: Sending RRD data '%s' (id %s, %d lines, %d group, %d group_method, %ld after, %ld before).",
+            w->id, st->name, st->id, lines, group_count, group_method, after, before);
+
+    time_t timestamp_in_data = rrd_stats_json(datasource_type, st, w->response.data, lines, group_count, group_method, (unsigned long)after, (unsigned long)before, nonzero);
+
+    if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
+        if(timestamp_in_data > last_timestamp_in_data)
+            buffer_strcat(w->response.data, "});");
+
+        else {
+            // the client already has the latest data
+            buffer_flush(w->response.data);
+            buffer_sprintf(w->response.data,
+                    "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'not_modified',message:'Data not modified'}]});",
+                    google_responseHandler, google_version, google_reqId);
+        }
+    }
+
+    return 200;
+}
+
+inline int web_client_api_old_data_request_json(RRDHOST *host, struct web_client *w, char *url) {
+    return web_client_api_old_data_request(host, w, url, DATASOURCE_JSON);
+}
+
+inline int web_client_api_old_data_request_jsonp(RRDHOST *host, struct web_client *w, char *url) {
+    return web_client_api_old_data_request(host, w, url, DATASOURCE_DATATABLE_JSONP);
+}
+
+inline int web_client_api_old_graph_request(RRDHOST *host, struct web_client *w, char *url) {
+    // get the name of the data to show
+    char *tok = mystrsep(&url, "/?&");
+    if(tok && *tok) {
+        debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
+
+        // do we have such a data set?
+        RRDSET *st = rrdset_find_byname(host, tok);
+        if(!st) st = rrdset_find(host, tok);
+        if(!st) {
+            // we don't have it
+            // try to send a file with that name
+            buffer_flush(w->response.data);
+            return mysendfile(w, tok);
+        }
+
+        debug(D_WEB_CLIENT_ACCESS, "%llu: Sending %s.json of RRD_STATS...", w->id, st->name);
+        w->response.data->contenttype = CT_APPLICATION_JSON;
+        buffer_flush(w->response.data);
+        rrd_stats_graph_json(st, url, w->response.data);
+        return 200;
+    }
+
+    buffer_flush(w->response.data);
+    buffer_strcat(w->response.data, "Graph name?\r\n");
+    return 400;
+}
+
+inline int web_client_api_old_list_request(RRDHOST *host, struct web_client *w, char *url) {
+    (void)url;
+
+    buffer_flush(w->response.data);
+    RRDSET *st;
+
+    rrdhost_rdlock(host);
+    rrdset_foreach_read(st, host) buffer_sprintf(w->response.data, "%s\n", st->name);
+    rrdhost_unlock(host);
+
+    return 200;
+}
+
+inline int web_client_api_old_all_json(RRDHOST *host, struct web_client *w, char *url) {
+    (void)url;
+
+    w->response.data->contenttype = CT_APPLICATION_JSON;
+    buffer_flush(w->response.data);
+    rrd_stats_all_json(host, w->response.data);
+    return 200;
+}
diff --git a/src/web_api_old.h b/src/web_api_old.h
new file mode 100644 (file)
index 0000000..dff48c2
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef NETDATA_WEB_API_OLD_H
+#define NETDATA_WEB_API_OLD_H
+
+#include "common.h"
+
+extern int web_client_api_old_data_request(RRDHOST *host, struct web_client *w, char *url, int datasource_type);
+extern int web_client_api_old_data_request_json(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_old_data_request_jsonp(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_old_graph_request(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_old_list_request(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_old_all_json(RRDHOST *host, struct web_client *w, char *url);
+
+#endif //NETDATA_WEB_API_OLD_H
diff --git a/src/web_api_v1.c b/src/web_api_v1.c
new file mode 100644 (file)
index 0000000..c180cf3
--- /dev/null
@@ -0,0 +1,902 @@
+#include "common.h"
+
+inline int web_client_api_request_v1_data_group(char *name, int def) {
+    if(!strcmp(name, "average"))
+        return GROUP_AVERAGE;
+
+    else if(!strcmp(name, "min"))
+        return GROUP_MIN;
+
+    else if(!strcmp(name, "max"))
+        return GROUP_MAX;
+
+    else if(!strcmp(name, "sum"))
+        return GROUP_SUM;
+
+    else if(!strcmp(name, "incremental-sum"))
+        return GROUP_INCREMENTAL_SUM;
+
+    return def;
+}
+
+inline uint32_t web_client_api_request_v1_data_options(char *o) {
+    uint32_t ret = 0x00000000;
+    char *tok;
+
+    while(o && *o && (tok = mystrsep(&o, ", |"))) {
+        if(!*tok) continue;
+
+        if(!strcmp(tok, "nonzero"))
+            ret |= RRDR_OPTION_NONZERO;
+        else if(!strcmp(tok, "flip") || !strcmp(tok, "reversed") || !strcmp(tok, "reverse"))
+            ret |= RRDR_OPTION_REVERSED;
+        else if(!strcmp(tok, "jsonwrap"))
+            ret |= RRDR_OPTION_JSON_WRAP;
+        else if(!strcmp(tok, "min2max"))
+            ret |= RRDR_OPTION_MIN2MAX;
+        else if(!strcmp(tok, "ms") || !strcmp(tok, "milliseconds"))
+            ret |= RRDR_OPTION_MILLISECONDS;
+        else if(!strcmp(tok, "abs") || !strcmp(tok, "absolute") || !strcmp(tok, "absolute_sum") || !strcmp(tok, "absolute-sum"))
+            ret |= RRDR_OPTION_ABSOLUTE;
+        else if(!strcmp(tok, "seconds"))
+            ret |= RRDR_OPTION_SECONDS;
+        else if(!strcmp(tok, "null2zero"))
+            ret |= RRDR_OPTION_NULL2ZERO;
+        else if(!strcmp(tok, "objectrows"))
+            ret |= RRDR_OPTION_OBJECTSROWS;
+        else if(!strcmp(tok, "google_json"))
+            ret |= RRDR_OPTION_GOOGLE_JSON;
+        else if(!strcmp(tok, "percentage"))
+            ret |= RRDR_OPTION_PERCENTAGE;
+        else if(!strcmp(tok, "unaligned"))
+            ret |= RRDR_OPTION_NOT_ALIGNED;
+    }
+
+    return ret;
+}
+
+inline uint32_t web_client_api_request_v1_data_format(char *name) {
+    if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSON)) // datatable
+        return DATASOURCE_DATATABLE_JSON;
+
+    else if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSONP)) // datasource
+        return DATASOURCE_DATATABLE_JSONP;
+
+    else if(!strcmp(name, DATASOURCE_FORMAT_JSON)) // json
+        return DATASOURCE_JSON;
+
+    else if(!strcmp(name, DATASOURCE_FORMAT_JSONP)) // jsonp
+        return DATASOURCE_JSONP;
+
+    else if(!strcmp(name, DATASOURCE_FORMAT_SSV)) // ssv
+        return DATASOURCE_SSV;
+
+    else if(!strcmp(name, DATASOURCE_FORMAT_CSV)) // csv
+        return DATASOURCE_CSV;
+
+    else if(!strcmp(name, DATASOURCE_FORMAT_TSV) || !strcmp(name, "tsv-excel")) // tsv
+        return DATASOURCE_TSV;
+
+    else if(!strcmp(name, DATASOURCE_FORMAT_HTML)) // html
+        return DATASOURCE_HTML;
+
+    else if(!strcmp(name, DATASOURCE_FORMAT_JS_ARRAY)) // array
+        return DATASOURCE_JS_ARRAY;
+
+    else if(!strcmp(name, DATASOURCE_FORMAT_SSV_COMMA)) // ssvcomma
+        return DATASOURCE_SSV_COMMA;
+
+    else if(!strcmp(name, DATASOURCE_FORMAT_CSV_JSON_ARRAY)) // csvjsonarray
+        return DATASOURCE_CSV_JSON_ARRAY;
+
+    return DATASOURCE_JSON;
+}
+
+inline uint32_t web_client_api_request_v1_data_google_format(char *name) {
+    if(!strcmp(name, "json"))
+        return DATASOURCE_DATATABLE_JSONP;
+
+    else if(!strcmp(name, "html"))
+        return DATASOURCE_HTML;
+
+    else if(!strcmp(name, "csv"))
+        return DATASOURCE_CSV;
+
+    else if(!strcmp(name, "tsv-excel"))
+        return DATASOURCE_TSV;
+
+    return DATASOURCE_JSON;
+}
+
+
+inline int web_client_api_request_v1_alarms(RRDHOST *host, struct web_client *w, char *url) {
+    int all = 0;
+
+    while(url) {
+        char *value = mystrsep(&url, "?&");
+        if (!value || !*value) continue;
+
+        if(!strcmp(value, "all")) all = 1;
+        else if(!strcmp(value, "active")) all = 0;
+    }
+
+    buffer_flush(w->response.data);
+    w->response.data->contenttype = CT_APPLICATION_JSON;
+    health_alarms2json(host, w->response.data, all);
+    return 200;
+}
+
+inline int web_client_api_request_v1_alarm_log(RRDHOST *host, struct web_client *w, char *url) {
+    uint32_t after = 0;
+
+    while(url) {
+        char *value = mystrsep(&url, "?&");
+        if (!value || !*value) continue;
+
+        char *name = mystrsep(&value, "=");
+        if(!name || !*name) continue;
+        if(!value || !*value) continue;
+
+        if(!strcmp(name, "after")) after = (uint32_t)strtoul(value, NULL, 0);
+    }
+
+    buffer_flush(w->response.data);
+    w->response.data->contenttype = CT_APPLICATION_JSON;
+    health_alarm_log2json(host, w->response.data, after);
+    return 200;
+}
+
+inline int web_client_api_request_single_chart(RRDHOST *host, struct web_client *w, char *url, void callback(RRDSET *st, BUFFER *buf)) {
+    int ret = 400;
+    char *chart = NULL;
+
+    buffer_flush(w->response.data);
+
+    while(url) {
+        char *value = mystrsep(&url, "?&");
+        if(!value || !*value) continue;
+
+        char *name = mystrsep(&value, "=");
+        if(!name || !*name) continue;
+        if(!value || !*value) continue;
+
+        // name and value are now the parameters
+        // they are not null and not empty
+
+        if(!strcmp(name, "chart")) chart = value;
+        //else {
+        /// buffer_sprintf(w->response.data, "Unknown parameter '%s' in request.", name);
+        //  goto cleanup;
+        //}
+    }
+
+    if(!chart || !*chart) {
+        buffer_sprintf(w->response.data, "No chart id is given at the request.");
+        goto cleanup;
+    }
+
+    RRDSET *st = rrdset_find(host, chart);
+    if(!st) st = rrdset_find_byname(host, chart);
+    if(!st) {
+        buffer_strcat(w->response.data, "Chart is not found: ");
+        buffer_strcat_htmlescape(w->response.data, chart);
+        ret = 404;
+        goto cleanup;
+    }
+
+    w->response.data->contenttype = CT_APPLICATION_JSON;
+    callback(st, w->response.data);
+    return 200;
+
+    cleanup:
+    return ret;
+}
+
+inline int web_client_api_request_v1_alarm_variables(RRDHOST *host, struct web_client *w, char *url) {
+    return web_client_api_request_single_chart(host, w, url, health_api_v1_chart_variables2json);
+}
+
+inline int web_client_api_request_v1_charts(RRDHOST *host, struct web_client *w, char *url) {
+    (void)url;
+
+    buffer_flush(w->response.data);
+    w->response.data->contenttype = CT_APPLICATION_JSON;
+    rrd_stats_api_v1_charts(host, w->response.data);
+    return 200;
+}
+
+inline int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client *w, char *url) {
+    int format = ALLMETRICS_SHELL;
+
+    while(url) {
+        char *value = mystrsep(&url, "?&");
+        if (!value || !*value) continue;
+
+        char *name = mystrsep(&value, "=");
+        if(!name || !*name) continue;
+        if(!value || !*value) continue;
+
+        if(!strcmp(name, "format")) {
+            if(!strcmp(value, ALLMETRICS_FORMAT_SHELL))
+                format = ALLMETRICS_SHELL;
+            else if(!strcmp(value, ALLMETRICS_FORMAT_PROMETHEUS))
+                format = ALLMETRICS_PROMETHEUS;
+            else
+                format = 0;
+        }
+    }
+
+    buffer_flush(w->response.data);
+    buffer_no_cacheable(w->response.data);
+
+    switch(format) {
+        case ALLMETRICS_SHELL:
+            w->response.data->contenttype = CT_TEXT_PLAIN;
+            rrd_stats_api_v1_charts_allmetrics_shell(host, w->response.data);
+            return 200;
+
+        case ALLMETRICS_PROMETHEUS:
+            w->response.data->contenttype = CT_PROMETHEUS;
+            rrd_stats_api_v1_charts_allmetrics_prometheus(host, w->response.data);
+            return 200;
+
+        default:
+            w->response.data->contenttype = CT_TEXT_PLAIN;
+            buffer_strcat(w->response.data, "Which format? Only '" ALLMETRICS_FORMAT_SHELL "' and '" ALLMETRICS_FORMAT_PROMETHEUS "' is currently supported.");
+            return 400;
+    }
+}
+
+inline int web_client_api_request_v1_chart(RRDHOST *host, struct web_client *w, char *url) {
+    return web_client_api_request_single_chart(host, w, url, rrd_stats_api_v1_chart);
+}
+
+int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *url) {
+    int ret = 400;
+    buffer_flush(w->response.data);
+
+    BUFFER *dimensions = NULL;
+
+    const char *chart = NULL
+    , *before_str = NULL
+    , *after_str = NULL
+    , *points_str = NULL
+    , *multiply_str = NULL
+    , *divide_str = NULL
+    , *label = NULL
+    , *units = NULL
+    , *label_color = NULL
+    , *value_color = NULL
+    , *refresh_str = NULL
+    , *precision_str = NULL
+    , *alarm = NULL;
+
+    int group = GROUP_AVERAGE;
+    uint32_t options = 0x00000000;
+
+    while(url) {
+        char *value = mystrsep(&url, "/?&");
+        if(!value || !*value) continue;
+
+        char *name = mystrsep(&value, "=");
+        if(!name || !*name) continue;
+        if(!value || !*value) continue;
+
+        debug(D_WEB_CLIENT, "%llu: API v1 badge.svg query param '%s' with value '%s'", w->id, name, value);
+
+        // name and value are now the parameters
+        // they are not null and not empty
+
+        if(!strcmp(name, "chart")) chart = value;
+        else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
+            if(!dimensions)
+                dimensions = buffer_create(100);
+
+            buffer_strcat(dimensions, "|");
+            buffer_strcat(dimensions, value);
+        }
+        else if(!strcmp(name, "after")) after_str = value;
+        else if(!strcmp(name, "before")) before_str = value;
+        else if(!strcmp(name, "points")) points_str = value;
+        else if(!strcmp(name, "group")) {
+            group = web_client_api_request_v1_data_group(value, GROUP_AVERAGE);
+        }
+        else if(!strcmp(name, "options")) {
+            options |= web_client_api_request_v1_data_options(value);
+        }
+        else if(!strcmp(name, "label")) label = value;
+        else if(!strcmp(name, "units")) units = value;
+        else if(!strcmp(name, "label_color")) label_color = value;
+        else if(!strcmp(name, "value_color")) value_color = value;
+        else if(!strcmp(name, "multiply")) multiply_str = value;
+        else if(!strcmp(name, "divide")) divide_str = value;
+        else if(!strcmp(name, "refresh")) refresh_str = value;
+        else if(!strcmp(name, "precision")) precision_str = value;
+        else if(!strcmp(name, "alarm")) alarm = value;
+    }
+
+    if(!chart || !*chart) {
+        buffer_no_cacheable(w->response.data);
+        buffer_sprintf(w->response.data, "No chart id is given at the request.");
+        goto cleanup;
+    }
+
+    RRDSET *st = rrdset_find(host, chart);
+    if(!st) st = rrdset_find_byname(host, chart);
+    if(!st) {
+        buffer_no_cacheable(w->response.data);
+        buffer_svg(w->response.data, "chart not found", NAN, "", NULL, NULL, -1);
+        ret = 200;
+        goto cleanup;
+    }
+
+    RRDCALC *rc = NULL;
+    if(alarm) {
+        rc = rrdcalc_find(st, alarm);
+        if (!rc) {
+            buffer_no_cacheable(w->response.data);
+            buffer_svg(w->response.data, "alarm not found", NAN, "", NULL, NULL, -1);
+            ret = 200;
+            goto cleanup;
+        }
+    }
+
+    long long multiply  = (multiply_str  && *multiply_str )?str2l(multiply_str):1;
+    long long divide    = (divide_str    && *divide_str   )?str2l(divide_str):1;
+    long long before    = (before_str    && *before_str   )?str2l(before_str):0;
+    long long after     = (after_str     && *after_str    )?str2l(after_str):-st->update_every;
+    int       points    = (points_str    && *points_str   )?str2i(points_str):1;
+    int       precision = (precision_str && *precision_str)?str2i(precision_str):-1;
+
+    if(!multiply) multiply = 1;
+    if(!divide) divide = 1;
+
+    int refresh = 0;
+    if(refresh_str && *refresh_str) {
+        if(!strcmp(refresh_str, "auto")) {
+            if(rc) refresh = rc->update_every;
+            else if(options & RRDR_OPTION_NOT_ALIGNED)
+                refresh = st->update_every;
+            else {
+                refresh = (int)(before - after);
+                if(refresh < 0) refresh = -refresh;
+            }
+        }
+        else {
+            refresh = str2i(refresh_str);
+            if(refresh < 0) refresh = -refresh;
+        }
+    }
+
+    if(!label) {
+        if(alarm) {
+            char *s = (char *)alarm;
+            while(*s) {
+                if(*s == '_') *s = ' ';
+                s++;
+            }
+            label = alarm;
+        }
+        else if(dimensions) {
+            const char *dim = buffer_tostring(dimensions);
+            if(*dim == '|') dim++;
+            label = dim;
+        }
+        else
+            label = st->name;
+    }
+    if(!units) {
+        if(alarm) {
+            if(rc->units)
+                units = rc->units;
+            else
+                units = "";
+        }
+        else if(options & RRDR_OPTION_PERCENTAGE)
+            units = "%";
+        else
+            units = st->units;
+    }
+
+    debug(D_WEB_CLIENT, "%llu: API command 'badge.svg' for chart '%s', alarm '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', options '0x%08x'"
+          , w->id
+          , chart
+          , alarm?alarm:""
+          , (dimensions)?buffer_tostring(dimensions):""
+          , after
+          , before
+          , points
+          , group
+          , options
+    );
+
+    if(rc) {
+        if (refresh > 0) {
+            buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
+            w->response.data->expires = now_realtime_sec() + refresh;
+        }
+        else buffer_no_cacheable(w->response.data);
+
+        if(!value_color) {
+            switch(rc->status) {
+                case RRDCALC_STATUS_CRITICAL:
+                    value_color = "red";
+                    break;
+
+                case RRDCALC_STATUS_WARNING:
+                    value_color = "orange";
+                    break;
+
+                case RRDCALC_STATUS_CLEAR:
+                    value_color = "brightgreen";
+                    break;
+
+                case RRDCALC_STATUS_UNDEFINED:
+                    value_color = "lightgrey";
+                    break;
+
+                case RRDCALC_STATUS_UNINITIALIZED:
+                    value_color = "#000";
+                    break;
+
+                default:
+                    value_color = "grey";
+                    break;
+            }
+        }
+
+        buffer_svg(w->response.data,
+                label,
+                (isnan(rc->value)||isinf(rc->value)) ? rc->value : rc->value * multiply / divide,
+                units,
+                label_color,
+                value_color,
+                precision);
+        ret = 200;
+    }
+    else {
+        time_t latest_timestamp = 0;
+        int value_is_null = 1;
+        calculated_number n = NAN;
+        ret = 500;
+
+        // if the collected value is too old, don't calculate its value
+        if (rrdset_last_entry_t(st) >= (now_realtime_sec() - (st->update_every * st->gap_when_lost_iterations_above)))
+            ret = rrd2value(st,
+                    w->response.data,
+                    &n,
+                    (dimensions) ? buffer_tostring(dimensions) : NULL,
+                    points,
+                    after,
+                    before,
+                    group,
+                    options,
+                    NULL,
+                    &latest_timestamp,
+                    &value_is_null);
+
+        // if the value cannot be calculated, show empty badge
+        if (ret != 200) {
+            buffer_no_cacheable(w->response.data);
+            value_is_null = 1;
+            n = 0;
+            ret = 200;
+        }
+        else if (refresh > 0) {
+            buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
+            w->response.data->expires = now_realtime_sec() + refresh;
+        }
+        else buffer_no_cacheable(w->response.data);
+
+        // render the badge
+        buffer_svg(w->response.data,
+                label,
+                (value_is_null)?NAN:(n * multiply / divide),
+                units,
+                label_color,
+                value_color,
+                precision);
+    }
+
+    cleanup:
+    buffer_free(dimensions);
+    return ret;
+}
+
+// returns the HTTP code
+inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, char *url) {
+    debug(D_WEB_CLIENT, "%llu: API v1 data with URL '%s'", w->id, url);
+
+    int ret = 400;
+    BUFFER *dimensions = NULL;
+
+    buffer_flush(w->response.data);
+
+    char    *google_version = "0.6",
+            *google_reqId = "0",
+            *google_sig = "0",
+            *google_out = "json",
+            *responseHandler = NULL,
+            *outFileName = NULL;
+
+    time_t last_timestamp_in_data = 0, google_timestamp = 0;
+
+    char *chart = NULL
+    , *before_str = NULL
+    , *after_str = NULL
+    , *points_str = NULL;
+
+    int group = GROUP_AVERAGE;
+    uint32_t format = DATASOURCE_JSON;
+    uint32_t options = 0x00000000;
+
+    while(url) {
+        char *value = mystrsep(&url, "?&");
+        if(!value || !*value) continue;
+
+        char *name = mystrsep(&value, "=");
+        if(!name || !*name) continue;
+        if(!value || !*value) continue;
+
+        debug(D_WEB_CLIENT, "%llu: API v1 data query param '%s' with value '%s'", w->id, name, value);
+
+        // name and value are now the parameters
+        // they are not null and not empty
+
+        if(!strcmp(name, "chart")) chart = value;
+        else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
+            if(!dimensions) dimensions = buffer_create(100);
+            buffer_strcat(dimensions, "|");
+            buffer_strcat(dimensions, value);
+        }
+        else if(!strcmp(name, "after")) after_str = value;
+        else if(!strcmp(name, "before")) before_str = value;
+        else if(!strcmp(name, "points")) points_str = value;
+        else if(!strcmp(name, "group")) {
+            group = web_client_api_request_v1_data_group(value, GROUP_AVERAGE);
+        }
+        else if(!strcmp(name, "format")) {
+            format = web_client_api_request_v1_data_format(value);
+        }
+        else if(!strcmp(name, "options")) {
+            options |= web_client_api_request_v1_data_options(value);
+        }
+        else if(!strcmp(name, "callback")) {
+            responseHandler = value;
+        }
+        else if(!strcmp(name, "filename")) {
+            outFileName = value;
+        }
+        else if(!strcmp(name, "tqx")) {
+            // parse Google Visualization API options
+            // https://developers.google.com/chart/interactive/docs/dev/implementing_data_source
+            char *tqx_name, *tqx_value;
+
+            while(value) {
+                tqx_value = mystrsep(&value, ";");
+                if(!tqx_value || !*tqx_value) continue;
+
+                tqx_name = mystrsep(&tqx_value, ":");
+                if(!tqx_name || !*tqx_name) continue;
+                if(!tqx_value || !*tqx_value) continue;
+
+                if(!strcmp(tqx_name, "version"))
+                    google_version = tqx_value;
+                else if(!strcmp(tqx_name, "reqId"))
+                    google_reqId = tqx_value;
+                else if(!strcmp(tqx_name, "sig")) {
+                    google_sig = tqx_value;
+                    google_timestamp = strtoul(google_sig, NULL, 0);
+                }
+                else if(!strcmp(tqx_name, "out")) {
+                    google_out = tqx_value;
+                    format = web_client_api_request_v1_data_google_format(google_out);
+                }
+                else if(!strcmp(tqx_name, "responseHandler"))
+                    responseHandler = tqx_value;
+                else if(!strcmp(tqx_name, "outFileName"))
+                    outFileName = tqx_value;
+            }
+        }
+    }
+
+    if(!chart || !*chart) {
+        buffer_sprintf(w->response.data, "No chart id is given at the request.");
+        goto cleanup;
+    }
+
+    RRDSET *st = rrdset_find(host, chart);
+    if(!st) st = rrdset_find_byname(host, chart);
+    if(!st) {
+        buffer_strcat(w->response.data, "Chart is not found: ");
+        buffer_strcat_htmlescape(w->response.data, chart);
+        ret = 404;
+        goto cleanup;
+    }
+
+    long long before = (before_str && *before_str)?str2l(before_str):0;
+    long long after  = (after_str  && *after_str) ?str2l(after_str):0;
+    int       points = (points_str && *points_str)?str2i(points_str):0;
+
+    debug(D_WEB_CLIENT, "%llu: API command 'data' for chart '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', format '%u', options '0x%08x'"
+          , w->id
+          , chart
+          , (dimensions)?buffer_tostring(dimensions):""
+          , after
+          , before
+          , points
+          , group
+          , format
+          , options
+    );
+
+    if(outFileName && *outFileName) {
+        buffer_sprintf(w->response.header, "Content-Disposition: attachment; filename=\"%s\"\r\n", outFileName);
+        debug(D_WEB_CLIENT, "%llu: generating outfilename header: '%s'", w->id, outFileName);
+    }
+
+    if(format == DATASOURCE_DATATABLE_JSONP) {
+        if(responseHandler == NULL)
+            responseHandler = "google.visualization.Query.setResponse";
+
+        debug(D_WEB_CLIENT_ACCESS, "%llu: GOOGLE JSON/JSONP: version = '%s', reqId = '%s', sig = '%s', out = '%s', responseHandler = '%s', outFileName = '%s'",
+                w->id, google_version, google_reqId, google_sig, google_out, responseHandler, outFileName
+        );
+
+        buffer_sprintf(w->response.data,
+                "%s({version:'%s',reqId:'%s',status:'ok',sig:'%ld',table:",
+                responseHandler, google_version, google_reqId, st->last_updated.tv_sec);
+    }
+    else if(format == DATASOURCE_JSONP) {
+        if(responseHandler == NULL)
+            responseHandler = "callback";
+
+        buffer_strcat(w->response.data, responseHandler);
+        buffer_strcat(w->response.data, "(");
+    }
+
+    ret = rrd2format(st, w->response.data, dimensions, format, points, after, before, group, options, &last_timestamp_in_data);
+
+    if(format == DATASOURCE_DATATABLE_JSONP) {
+        if(google_timestamp < last_timestamp_in_data)
+            buffer_strcat(w->response.data, "});");
+
+        else {
+            // the client already has the latest data
+            buffer_flush(w->response.data);
+            buffer_sprintf(w->response.data,
+                    "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'not_modified',message:'Data not modified'}]});",
+                    responseHandler, google_version, google_reqId);
+        }
+    }
+    else if(format == DATASOURCE_JSONP)
+        buffer_strcat(w->response.data, ");");
+
+    cleanup:
+    buffer_free(dimensions);
+    return ret;
+}
+
+inline int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *w, char *url) {
+    static uint32_t hash_action = 0, hash_access = 0, hash_hello = 0, hash_delete = 0, hash_search = 0,
+            hash_switch = 0, hash_machine = 0, hash_url = 0, hash_name = 0, hash_delete_url = 0, hash_for = 0,
+            hash_to = 0 /*, hash_redirects = 0 */;
+
+    if(unlikely(!hash_action)) {
+        hash_action = simple_hash("action");
+        hash_access = simple_hash("access");
+        hash_hello = simple_hash("hello");
+        hash_delete = simple_hash("delete");
+        hash_search = simple_hash("search");
+        hash_switch = simple_hash("switch");
+        hash_machine = simple_hash("machine");
+        hash_url = simple_hash("url");
+        hash_name = simple_hash("name");
+        hash_delete_url = simple_hash("delete_url");
+        hash_for = simple_hash("for");
+        hash_to = simple_hash("to");
+/*
+        hash_redirects = simple_hash("redirects");
+*/
+    }
+
+    char person_guid[GUID_LEN + 1] = "";
+
+    debug(D_WEB_CLIENT, "%llu: API v1 registry with URL '%s'", w->id, url);
+
+    // FIXME
+    // The browser may send multiple cookies with our id
+
+    char *cookie = strstr(w->response.data->buffer, NETDATA_REGISTRY_COOKIE_NAME "=");
+    if(cookie)
+        strncpyz(person_guid, &cookie[sizeof(NETDATA_REGISTRY_COOKIE_NAME)], 36);
+
+    char action = '\0';
+    char *machine_guid = NULL,
+            *machine_url = NULL,
+            *url_name = NULL,
+            *search_machine_guid = NULL,
+            *delete_url = NULL,
+            *to_person_guid = NULL;
+/*
+    int redirects = 0;
+*/
+
+    while(url) {
+        char *value = mystrsep(&url, "?&");
+        if (!value || !*value) continue;
+
+        char *name = mystrsep(&value, "=");
+        if (!name || !*name) continue;
+        if (!value || !*value) continue;
+
+        debug(D_WEB_CLIENT, "%llu: API v1 registry query param '%s' with value '%s'", w->id, name, value);
+
+        uint32_t hash = simple_hash(name);
+
+        if(hash == hash_action && !strcmp(name, "action")) {
+            uint32_t vhash = simple_hash(value);
+
+            if(vhash == hash_access && !strcmp(value, "access")) action = 'A';
+            else if(vhash == hash_hello && !strcmp(value, "hello")) action = 'H';
+            else if(vhash == hash_delete && !strcmp(value, "delete")) action = 'D';
+            else if(vhash == hash_search && !strcmp(value, "search")) action = 'S';
+            else if(vhash == hash_switch && !strcmp(value, "switch")) action = 'W';
+#ifdef NETDATA_INTERNAL_CHECKS
+            else error("unknown registry action '%s'", value);
+#endif /* NETDATA_INTERNAL_CHECKS */
+        }
+/*
+        else if(hash == hash_redirects && !strcmp(name, "redirects"))
+            redirects = atoi(value);
+*/
+        else if(hash == hash_machine && !strcmp(name, "machine"))
+            machine_guid = value;
+
+        else if(hash == hash_url && !strcmp(name, "url"))
+            machine_url = value;
+
+        else if(action == 'A') {
+            if(hash == hash_name && !strcmp(name, "name"))
+                url_name = value;
+        }
+        else if(action == 'D') {
+            if(hash == hash_delete_url && !strcmp(name, "delete_url"))
+                delete_url = value;
+        }
+        else if(action == 'S') {
+            if(hash == hash_for && !strcmp(name, "for"))
+                search_machine_guid = value;
+        }
+        else if(action == 'W') {
+            if(hash == hash_to && !strcmp(name, "to"))
+                to_person_guid = value;
+        }
+#ifdef NETDATA_INTERNAL_CHECKS
+        else error("unused registry URL parameter '%s' with value '%s'", name, value);
+#endif /* NETDATA_INTERNAL_CHECKS */
+    }
+
+    if(respect_web_browser_do_not_track_policy && w->donottrack) {
+        buffer_flush(w->response.data);
+        buffer_sprintf(w->response.data, "Your web browser is sending 'DNT: 1' (Do Not Track). The registry requires persistent cookies on your browser to work.");
+        return 400;
+    }
+
+    if(action == 'A' && (!machine_guid || !machine_url || !url_name)) {
+        error("Invalid registry request - access requires these parameters: machine ('%s'), url ('%s'), name ('%s')",
+                machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", url_name?url_name:"UNSET");
+        buffer_flush(w->response.data);
+        buffer_strcat(w->response.data, "Invalid registry Access request.");
+        return 400;
+    }
+    else if(action == 'D' && (!machine_guid || !machine_url || !delete_url)) {
+        error("Invalid registry request - delete requires these parameters: machine ('%s'), url ('%s'), delete_url ('%s')",
+                machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", delete_url?delete_url:"UNSET");
+        buffer_flush(w->response.data);
+        buffer_strcat(w->response.data, "Invalid registry Delete request.");
+        return 400;
+    }
+    else if(action == 'S' && (!machine_guid || !machine_url || !search_machine_guid)) {
+        error("Invalid registry request - search requires these parameters: machine ('%s'), url ('%s'), for ('%s')",
+                machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", search_machine_guid?search_machine_guid:"UNSET");
+        buffer_flush(w->response.data);
+        buffer_strcat(w->response.data, "Invalid registry Search request.");
+        return 400;
+    }
+    else if(action == 'W' && (!machine_guid || !machine_url || !to_person_guid)) {
+        error("Invalid registry request - switching identity requires these parameters: machine ('%s'), url ('%s'), to ('%s')",
+                machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", to_person_guid?to_person_guid:"UNSET");
+        buffer_flush(w->response.data);
+        buffer_strcat(w->response.data, "Invalid registry Switch request.");
+        return 400;
+    }
+
+    switch(action) {
+        case 'A':
+            w->tracking_required = 1;
+            return registry_request_access_json(host, w, person_guid, machine_guid, machine_url, url_name, now_realtime_sec());
+
+        case 'D':
+            w->tracking_required = 1;
+            return registry_request_delete_json(host, w, person_guid, machine_guid, machine_url, delete_url, now_realtime_sec());
+
+        case 'S':
+            w->tracking_required = 1;
+            return registry_request_search_json(host, w, person_guid, machine_guid, machine_url, search_machine_guid, now_realtime_sec());
+
+        case 'W':
+            w->tracking_required = 1;
+            return registry_request_switch_json(host, w, person_guid, machine_guid, machine_url, to_person_guid, now_realtime_sec());
+
+        case 'H':
+            return registry_request_hello_json(host, w);
+
+        default:
+            buffer_flush(w->response.data);
+            buffer_strcat(w->response.data, "Invalid registry request - you need to set an action: hello, access, delete, search");
+            return 400;
+    }
+}
+
+inline int web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *url) {
+    static uint32_t hash_data = 0, hash_chart = 0, hash_charts = 0, hash_registry = 0, hash_badge = 0, hash_alarms = 0, hash_alarm_log = 0, hash_alarm_variables = 0, hash_raw = 0;
+
+    if(unlikely(hash_data == 0)) {
+        hash_data = simple_hash("data");
+        hash_chart = simple_hash("chart");
+        hash_charts = simple_hash("charts");
+        hash_registry = simple_hash("registry");
+        hash_badge = simple_hash("badge.svg");
+        hash_alarms = simple_hash("alarms");
+        hash_alarm_log = simple_hash("alarm_log");
+        hash_alarm_variables = simple_hash("alarm_variables");
+        hash_raw = simple_hash("allmetrics");
+    }
+
+    // get the command
+    char *tok = mystrsep(&url, "/?&");
+    if(tok && *tok) {
+        debug(D_WEB_CLIENT, "%llu: Searching for API v1 command '%s'.", w->id, tok);
+        uint32_t hash = simple_hash(tok);
+
+        if(hash == hash_data && !strcmp(tok, "data"))
+            return web_client_api_request_v1_data(host, w, url);
+
+        else if(hash == hash_chart && !strcmp(tok, "chart"))
+            return web_client_api_request_v1_chart(host, w, url);
+
+        else if(hash == hash_charts && !strcmp(tok, "charts"))
+            return web_client_api_request_v1_charts(host, w, url);
+
+        else if(hash == hash_registry && !strcmp(tok, "registry"))
+            return web_client_api_request_v1_registry(host, w, url);
+
+        else if(hash == hash_badge && !strcmp(tok, "badge.svg"))
+            return web_client_api_request_v1_badge(host, w, url);
+
+        else if(hash == hash_alarms && !strcmp(tok, "alarms"))
+            return web_client_api_request_v1_alarms(host, w, url);
+
+        else if(hash == hash_alarm_log && !strcmp(tok, "alarm_log"))
+            return web_client_api_request_v1_alarm_log(host, w, url);
+
+        else if(hash == hash_alarm_variables && !strcmp(tok, "alarm_variables"))
+            return web_client_api_request_v1_alarm_variables(host, w, url);
+
+        else if(hash == hash_raw && !strcmp(tok, "allmetrics"))
+            return web_client_api_request_v1_allmetrics(host, w, url);
+
+        else {
+            buffer_flush(w->response.data);
+            buffer_strcat(w->response.data, "Unsupported v1 API command: ");
+            buffer_strcat_htmlescape(w->response.data, tok);
+            return 404;
+        }
+    }
+    else {
+        buffer_flush(w->response.data);
+        buffer_sprintf(w->response.data, "Which API v1 command?");
+        return 400;
+    }
+}
diff --git a/src/web_api_v1.h b/src/web_api_v1.h
new file mode 100644 (file)
index 0000000..e980edb
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef NETDATA_WEB_API_V1_H
+#define NETDATA_WEB_API_V1_H
+
+extern int web_client_api_request_v1_data_group(char *name, int def);
+extern uint32_t web_client_api_request_v1_data_options(char *o);
+extern uint32_t web_client_api_request_v1_data_format(char *name);
+extern uint32_t web_client_api_request_v1_data_google_format(char *name);
+
+extern int web_client_api_request_v1_alarms(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_request_v1_alarm_log(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_request_single_chart(RRDHOST *host, struct web_client *w, char *url, void callback(RRDSET *st, BUFFER *buf));
+extern int web_client_api_request_v1_alarm_variables(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_request_v1_charts(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_request_v1_chart(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *w, char *url);
+extern int web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *url);
+
+#endif //NETDATA_WEB_API_V1_H
index 6203db0f73e539066dd9aad65eb854a14ffd5bb6..9f9ceda63da26a5f3d9720c863494dabe46755b8 100644 (file)
@@ -359,8 +359,9 @@ BUFFER *buffer_create(size_t size)
     return(b);
 }
 
-void buffer_free(BUFFER *b)
-{
+void buffer_free(BUFFER *b) {
+    if(unlikely(!b)) return;
+
     buffer_overflow_check(b);
 
     debug(D_WEB_BUFFER, "Freeing web buffer of size %zu.", b->size);
index 9a9e2376608a964f19448dea8c3c757603fe3099..ef39d3b5834e92ff2444848816fca0e6db94f11b 100644 (file)
@@ -45,8 +45,7 @@ static inline int web_client_uncrock_socket(struct web_client *w) {
     return 0;
 }
 
-struct web_client *web_client_create(int listener)
-{
+struct web_client *web_client_create(int listener) {
     struct web_client *w;
 
     w = callocz(1, sizeof(struct web_client));
@@ -223,9 +222,9 @@ struct web_client *web_client_free(struct web_client *w) {
 
     if(w->prev) w->prev->next = w->next;
     if(w->next) w->next->prev = w->prev;
-    if(w->response.header_output) buffer_free(w->response.header_output);
-    if(w->response.header) buffer_free(w->response.header);
-    if(w->response.data) buffer_free(w->response.data);
+    buffer_free(w->response.header_output);
+    buffer_free(w->response.header);
+    buffer_free(w->response.data);
     if(w->ifd != -1) close(w->ifd);
     if(w->ofd != -1 && w->ofd != w->ifd) close(w->ofd);
     freez(w);
@@ -240,7 +239,7 @@ uid_t web_files_uid(void) {
     static uid_t owner_uid = 0;
 
     if(unlikely(!web_owner)) {
-        web_owner = config_get("global", "web files owner", config_get("global", "run as user", ""));
+        web_owner = config_get(CONFIG_SECTION_WEB, "web files owner", config_get(CONFIG_SECTION_GLOBAL, "run as user", ""));
         if(!web_owner || !*web_owner)
             owner_uid = geteuid();
         else {
@@ -267,7 +266,7 @@ gid_t web_files_gid(void) {
     static gid_t owner_gid = 0;
 
     if(unlikely(!web_group)) {
-        web_group = config_get("global", "web files group", config_get("global", "web files owner", ""));
+        web_group = config_get(CONFIG_SECTION_WEB, "web files group", config_get(CONFIG_SECTION_WEB, "web files owner", ""));
         if(!web_group || !*web_group)
             owner_gid = getegid();
         else {
@@ -289,8 +288,7 @@ gid_t web_files_gid(void) {
     return(owner_gid);
 }
 
-int mysendfile(struct web_client *w, char *filename)
-{
+int mysendfile(struct web_client *w, char *filename) {
     debug(D_WEB_CLIENT, "%llu: Looking for file '%s/%s'", w->id, netdata_configured_web_dir, filename);
 
     // skip leading slashes
@@ -304,6 +302,7 @@ int mysendfile(struct web_client *w, char *filename)
     for(s = filename; *s ;s++) {
         if( !isalnum(*s) && *s != '/' && *s != '.' && *s != '-' && *s != '_') {
             debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
+            w->response.data->contenttype = CT_TEXT_HTML;
             buffer_sprintf(w->response.data, "Filename contains invalid characters: ");
             buffer_strcat_htmlescape(w->response.data, filename);
             return 400;
@@ -313,6 +312,7 @@ int mysendfile(struct web_client *w, char *filename)
     // if the filename contains a .. refuse to serve it
     if(strstr(filename, "..") != 0) {
         debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
+        w->response.data->contenttype = CT_TEXT_HTML;
         buffer_strcat(w->response.data, "Relative filenames are not supported: ");
         buffer_strcat_htmlescape(w->response.data, filename);
         return 400;
@@ -326,6 +326,7 @@ int mysendfile(struct web_client *w, char *filename)
     struct stat stat;
     if(lstat(webfilename, &stat) != 0) {
         debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not found.", w->id, webfilename);
+        w->response.data->contenttype = CT_TEXT_HTML;
         buffer_strcat(w->response.data, "File does not exist, or is not accessible: ");
         buffer_strcat_htmlescape(w->response.data, webfilename);
         return 404;
@@ -334,6 +335,7 @@ int mysendfile(struct web_client *w, char *filename)
     // check if the file is owned by expected user
     if(stat.st_uid != web_files_uid()) {
         error("%llu: File '%s' is owned by user %u (expected user %u). Access Denied.", w->id, webfilename, stat.st_uid, web_files_uid());
+        w->response.data->contenttype = CT_TEXT_HTML;
         buffer_strcat(w->response.data, "Access to file is not permitted: ");
         buffer_strcat_htmlescape(w->response.data, webfilename);
         return 403;
@@ -342,6 +344,7 @@ int mysendfile(struct web_client *w, char *filename)
     // check if the file is owned by expected group
     if(stat.st_gid != web_files_gid()) {
         error("%llu: File '%s' is owned by group %u (expected group %u). Access Denied.", w->id, webfilename, stat.st_gid, web_files_gid());
+        w->response.data->contenttype = CT_TEXT_HTML;
         buffer_strcat(w->response.data, "Access to file is not permitted: ");
         buffer_strcat_htmlescape(w->response.data, webfilename);
         return 403;
@@ -354,6 +357,7 @@ int mysendfile(struct web_client *w, char *filename)
 
     if((stat.st_mode & S_IFMT) != S_IFREG) {
         error("%llu: File '%s' is not a regular file. Access Denied.", w->id, webfilename);
+        w->response.data->contenttype = CT_TEXT_HTML;
         buffer_strcat(w->response.data, "Access to file is not permitted: ");
         buffer_strcat_htmlescape(w->response.data, webfilename);
         return 403;
@@ -366,6 +370,7 @@ int mysendfile(struct web_client *w, char *filename)
 
         if(errno == EBUSY || errno == EAGAIN) {
             error("%llu: File '%s' is busy, sending 307 Moved Temporarily to force retry.", w->id, webfilename);
+            w->response.data->contenttype = CT_TEXT_HTML;
             buffer_sprintf(w->response.header, "Location: /" WEB_PATH_FILE "/%s\r\n", filename);
             buffer_strcat(w->response.data, "File is currently busy, please try again later: ");
             buffer_strcat_htmlescape(w->response.data, webfilename);
@@ -373,6 +378,7 @@ int mysendfile(struct web_client *w, char *filename)
         }
         else {
             error("%llu: Cannot open file '%s'.", w->id, webfilename);
+            w->response.data->contenttype = CT_TEXT_HTML;
             buffer_strcat(w->response.data, "Cannot open file: ");
             buffer_strcat_htmlescape(w->response.data, webfilename);
             return 404;
@@ -532,98 +538,6 @@ void buffer_data_options2string(BUFFER *wb, uint32_t options) {
     }
 }
 
-uint32_t web_client_api_request_v1_data_options(char *o)
-{
-    uint32_t ret = 0x00000000;
-    char *tok;
-
-    while(o && *o && (tok = mystrsep(&o, ", |"))) {
-        if(!*tok) continue;
-
-        if(!strcmp(tok, "nonzero"))
-            ret |= RRDR_OPTION_NONZERO;
-        else if(!strcmp(tok, "flip") || !strcmp(tok, "reversed") || !strcmp(tok, "reverse"))
-            ret |= RRDR_OPTION_REVERSED;
-        else if(!strcmp(tok, "jsonwrap"))
-            ret |= RRDR_OPTION_JSON_WRAP;
-        else if(!strcmp(tok, "min2max"))
-            ret |= RRDR_OPTION_MIN2MAX;
-        else if(!strcmp(tok, "ms") || !strcmp(tok, "milliseconds"))
-            ret |= RRDR_OPTION_MILLISECONDS;
-        else if(!strcmp(tok, "abs") || !strcmp(tok, "absolute") || !strcmp(tok, "absolute_sum") || !strcmp(tok, "absolute-sum"))
-            ret |= RRDR_OPTION_ABSOLUTE;
-        else if(!strcmp(tok, "seconds"))
-            ret |= RRDR_OPTION_SECONDS;
-        else if(!strcmp(tok, "null2zero"))
-            ret |= RRDR_OPTION_NULL2ZERO;
-        else if(!strcmp(tok, "objectrows"))
-            ret |= RRDR_OPTION_OBJECTSROWS;
-        else if(!strcmp(tok, "google_json"))
-            ret |= RRDR_OPTION_GOOGLE_JSON;
-        else if(!strcmp(tok, "percentage"))
-            ret |= RRDR_OPTION_PERCENTAGE;
-        else if(!strcmp(tok, "unaligned"))
-            ret |= RRDR_OPTION_NOT_ALIGNED;
-    }
-
-    return ret;
-}
-
-uint32_t web_client_api_request_v1_data_format(char *name)
-{
-    if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSON)) // datatable
-        return DATASOURCE_DATATABLE_JSON;
-
-    else if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSONP)) // datasource
-        return DATASOURCE_DATATABLE_JSONP;
-
-    else if(!strcmp(name, DATASOURCE_FORMAT_JSON)) // json
-        return DATASOURCE_JSON;
-
-    else if(!strcmp(name, DATASOURCE_FORMAT_JSONP)) // jsonp
-        return DATASOURCE_JSONP;
-
-    else if(!strcmp(name, DATASOURCE_FORMAT_SSV)) // ssv
-        return DATASOURCE_SSV;
-
-    else if(!strcmp(name, DATASOURCE_FORMAT_CSV)) // csv
-        return DATASOURCE_CSV;
-
-    else if(!strcmp(name, DATASOURCE_FORMAT_TSV) || !strcmp(name, "tsv-excel")) // tsv
-        return DATASOURCE_TSV;
-
-    else if(!strcmp(name, DATASOURCE_FORMAT_HTML)) // html
-        return DATASOURCE_HTML;
-
-    else if(!strcmp(name, DATASOURCE_FORMAT_JS_ARRAY)) // array
-        return DATASOURCE_JS_ARRAY;
-
-    else if(!strcmp(name, DATASOURCE_FORMAT_SSV_COMMA)) // ssvcomma
-        return DATASOURCE_SSV_COMMA;
-
-    else if(!strcmp(name, DATASOURCE_FORMAT_CSV_JSON_ARRAY)) // csvjsonarray
-        return DATASOURCE_CSV_JSON_ARRAY;
-
-    return DATASOURCE_JSON;
-}
-
-uint32_t web_client_api_request_v1_data_google_format(char *name)
-{
-    if(!strcmp(name, "json"))
-        return DATASOURCE_DATATABLE_JSONP;
-
-    else if(!strcmp(name, "html"))
-        return DATASOURCE_HTML;
-
-    else if(!strcmp(name, "csv"))
-        return DATASOURCE_CSV;
-
-    else if(!strcmp(name, "tsv-excel"))
-        return DATASOURCE_TSV;
-
-    return DATASOURCE_JSON;
-}
-
 const char *group_method2string(int group) {
     switch(group) {
         case GROUP_UNDEFINED:
@@ -649,839 +563,27 @@ const char *group_method2string(int group) {
     }
 }
 
-int web_client_api_request_v1_data_group(char *name, int def)
-{
-    if(!strcmp(name, "average"))
-        return GROUP_AVERAGE;
-
-    else if(!strcmp(name, "min"))
-        return GROUP_MIN;
-
-    else if(!strcmp(name, "max"))
-        return GROUP_MAX;
-
-    else if(!strcmp(name, "sum"))
-        return GROUP_SUM;
-
-    else if(!strcmp(name, "incremental-sum"))
-        return GROUP_INCREMENTAL_SUM;
-
-    return def;
-}
-
-int web_client_api_request_v1_alarms(struct web_client *w, char *url)
-{
-    int all = 0;
-
-    while(url) {
-        char *value = mystrsep(&url, "?&");
-        if (!value || !*value) continue;
-
-        if(!strcmp(value, "all")) all = 1;
-        else if(!strcmp(value, "active")) all = 0;
-    }
-
-    buffer_flush(w->response.data);
-    w->response.data->contenttype = CT_APPLICATION_JSON;
-    health_alarms2json(&localhost, w->response.data, all);
-    return 200;
-}
-
-int web_client_api_request_v1_alarm_log(struct web_client *w, char *url)
-{
-    uint32_t after = 0;
-
-    while(url) {
-        char *value = mystrsep(&url, "?&");
-        if (!value || !*value) continue;
-
-        char *name = mystrsep(&value, "=");
-        if(!name || !*name) continue;
-        if(!value || !*value) continue;
-
-        if(!strcmp(name, "after")) after = strtoul(value, NULL, 0);
-    }
-
-    buffer_flush(w->response.data);
-    w->response.data->contenttype = CT_APPLICATION_JSON;
-    health_alarm_log2json(&localhost, w->response.data, after);
-    return 200;
-}
-
-int web_client_api_request_single_chart(struct web_client *w, char *url, void callback(RRDSET *st, BUFFER *buf))
-{
-    int ret = 400;
-    char *chart = NULL;
-
-    buffer_flush(w->response.data);
-
-    while(url) {
-        char *value = mystrsep(&url, "?&");
-        if(!value || !*value) continue;
-
-        char *name = mystrsep(&value, "=");
-        if(!name || !*name) continue;
-        if(!value || !*value) continue;
-
-        // name and value are now the parameters
-        // they are not null and not empty
-
-        if(!strcmp(name, "chart")) chart = value;
-        //else {
-        /// buffer_sprintf(w->response.data, "Unknown parameter '%s' in request.", name);
-        //  goto cleanup;
-        //}
-    }
-
-    if(!chart || !*chart) {
-        buffer_sprintf(w->response.data, "No chart id is given at the request.");
-        goto cleanup;
-    }
-
-    RRDSET *st = rrdset_find(chart);
-    if(!st) st = rrdset_find_byname(chart);
-    if(!st) {
-        buffer_strcat(w->response.data, "Chart is not found: ");
-        buffer_strcat_htmlescape(w->response.data, chart);
-        ret = 404;
-        goto cleanup;
-    }
-
-    w->response.data->contenttype = CT_APPLICATION_JSON;
-    callback(st, w->response.data);
-    return 200;
-
-    cleanup:
-    return ret;
-}
-
-int web_client_api_request_v1_alarm_variables(struct web_client *w, char *url)
-{
-    return web_client_api_request_single_chart(w, url, health_api_v1_chart_variables2json);
-}
-
-int web_client_api_request_v1_charts(struct web_client *w, char *url)
-{
-    (void)url;
-
-    buffer_flush(w->response.data);
-    w->response.data->contenttype = CT_APPLICATION_JSON;
-    rrd_stats_api_v1_charts(w->response.data);
-    return 200;
-}
-
-int web_client_api_request_v1_allmetrics(struct web_client *w, char *url)
-{
-    int format = ALLMETRICS_SHELL;
-
-    while(url) {
-        char *value = mystrsep(&url, "?&");
-        if (!value || !*value) continue;
-
-        char *name = mystrsep(&value, "=");
-        if(!name || !*name) continue;
-        if(!value || !*value) continue;
-
-        if(!strcmp(name, "format")) {
-            if(!strcmp(value, ALLMETRICS_FORMAT_SHELL))
-                format = ALLMETRICS_SHELL;
-            else if(!strcmp(value, ALLMETRICS_FORMAT_PROMETHEUS))
-                format = ALLMETRICS_PROMETHEUS;
-            else
-                format = 0;
-        }
-    }
-
-    buffer_flush(w->response.data);
-    buffer_no_cacheable(w->response.data);
-
-    switch(format) {
-        case ALLMETRICS_SHELL:
-            w->response.data->contenttype = CT_TEXT_PLAIN;
-            rrd_stats_api_v1_charts_allmetrics_shell(w->response.data);
-            return 200;
-
-        case ALLMETRICS_PROMETHEUS:
-            w->response.data->contenttype = CT_PROMETHEUS;
-            rrd_stats_api_v1_charts_allmetrics_prometheus(w->response.data);
-            return 200;
-
-        default:
-            w->response.data->contenttype = CT_TEXT_PLAIN;
-            buffer_strcat(w->response.data, "Which format? Only '" ALLMETRICS_FORMAT_SHELL "' and '" ALLMETRICS_FORMAT_PROMETHEUS "' is currently supported.");
-            return 400;
-    }
-}
-
-int web_client_api_request_v1_chart(struct web_client *w, char *url)
-{
-    return web_client_api_request_single_chart(w, url, rrd_stats_api_v1_chart);
-}
-
-int web_client_api_request_v1_badge(struct web_client *w, char *url) {
-    int ret = 400;
-    buffer_flush(w->response.data);
-
-    BUFFER *dimensions = NULL;
-    
-    const char *chart = NULL
-            , *before_str = NULL
-            , *after_str = NULL
-            , *points_str = NULL
-            , *multiply_str = NULL
-            , *divide_str = NULL
-            , *label = NULL
-            , *units = NULL
-            , *label_color = NULL
-            , *value_color = NULL
-            , *refresh_str = NULL
-            , *precision_str = NULL
-            , *alarm = NULL;
-
-    int group = GROUP_AVERAGE;
-    uint32_t options = 0x00000000;
-
-    while(url) {
-        char *value = mystrsep(&url, "/?&");
-        if(!value || !*value) continue;
-
-        char *name = mystrsep(&value, "=");
-        if(!name || !*name) continue;
-        if(!value || !*value) continue;
-
-        debug(D_WEB_CLIENT, "%llu: API v1 badge.svg query param '%s' with value '%s'", w->id, name, value);
-
-        // name and value are now the parameters
-        // they are not null and not empty
-
-        if(!strcmp(name, "chart")) chart = value;
-        else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
-            if(!dimensions)
-                dimensions = buffer_create(100);
-
-            buffer_strcat(dimensions, "|");
-            buffer_strcat(dimensions, value);
-        }
-        else if(!strcmp(name, "after")) after_str = value;
-        else if(!strcmp(name, "before")) before_str = value;
-        else if(!strcmp(name, "points")) points_str = value;
-        else if(!strcmp(name, "group")) {
-            group = web_client_api_request_v1_data_group(value, GROUP_AVERAGE);
-        }
-        else if(!strcmp(name, "options")) {
-            options |= web_client_api_request_v1_data_options(value);
-        }
-        else if(!strcmp(name, "label")) label = value;
-        else if(!strcmp(name, "units")) units = value;
-        else if(!strcmp(name, "label_color")) label_color = value;
-        else if(!strcmp(name, "value_color")) value_color = value;
-        else if(!strcmp(name, "multiply")) multiply_str = value;
-        else if(!strcmp(name, "divide")) divide_str = value;
-        else if(!strcmp(name, "refresh")) refresh_str = value;
-        else if(!strcmp(name, "precision")) precision_str = value;
-        else if(!strcmp(name, "alarm")) alarm = value;
-    }
-
-    if(!chart || !*chart) {
-        buffer_no_cacheable(w->response.data);
-        buffer_sprintf(w->response.data, "No chart id is given at the request.");
-        goto cleanup;
-    }
-
-    RRDSET *st = rrdset_find(chart);
-    if(!st) st = rrdset_find_byname(chart);
-    if(!st) {
-        buffer_no_cacheable(w->response.data);
-        buffer_svg(w->response.data, "chart not found", NAN, "", NULL, NULL, -1);
-        ret = 200;
-        goto cleanup;
-    }
-
-    RRDCALC *rc = NULL;
-    if(alarm) {
-        rc = rrdcalc_find(st, alarm);
-        if (!rc) {
-            buffer_no_cacheable(w->response.data);
-            buffer_svg(w->response.data, "alarm not found", NAN, "", NULL, NULL, -1);
-            ret = 200;
-            goto cleanup;
-        }
-    }
-
-    long long multiply  = (multiply_str  && *multiply_str )?str2l(multiply_str):1;
-    long long divide    = (divide_str    && *divide_str   )?str2l(divide_str):1;
-    long long before    = (before_str    && *before_str   )?str2l(before_str):0;
-    long long after     = (after_str     && *after_str    )?str2l(after_str):-st->update_every;
-    int       points    = (points_str    && *points_str   )?str2i(points_str):1;
-    int       precision = (precision_str && *precision_str)?str2i(precision_str):-1;
-
-    if(!multiply) multiply = 1;
-    if(!divide) divide = 1;
-
-    int refresh = 0;
-    if(refresh_str && *refresh_str) {
-        if(!strcmp(refresh_str, "auto")) {
-            if(rc) refresh = rc->update_every;
-            else if(options & RRDR_OPTION_NOT_ALIGNED)
-                refresh = st->update_every;
-            else {
-                refresh = (int)(before - after);
-                if(refresh < 0) refresh = -refresh;
-            }
-        }
-        else {
-            refresh = str2i(refresh_str);
-            if(refresh < 0) refresh = -refresh;
-        }
-    }
-
-    if(!label) {
-        if(alarm) {
-            char *s = (char *)alarm;
-            while(*s) {
-                if(*s == '_') *s = ' ';
-                s++;
-            }
-            label = alarm;
-        }
-        else if(dimensions) {
-            const char *dim = buffer_tostring(dimensions);
-            if(*dim == '|') dim++;
-            label = dim;
-        }
-        else
-            label = st->name;
-    }
-    if(!units) {
-        if(alarm) {
-            if(rc->units)
-                units = rc->units;
-            else
-                units = "";
-        }
-        else if(options & RRDR_OPTION_PERCENTAGE)
-            units = "%";
-        else
-            units = st->units;
-    }
-
-    debug(D_WEB_CLIENT, "%llu: API command 'badge.svg' for chart '%s', alarm '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', options '0x%08x'"
-            , w->id
-            , chart
-            , alarm?alarm:""
-            , (dimensions)?buffer_tostring(dimensions):""
-            , after
-            , before
-            , points
-            , group
-            , options
-            );
-
-    if(rc) {
-        if (refresh > 0) {
-            buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
-            w->response.data->expires = now_realtime_sec() + refresh;
-        }
-        else buffer_no_cacheable(w->response.data);
-
-        if(!value_color) {
-            switch(rc->status) {
-                case RRDCALC_STATUS_CRITICAL:
-                    value_color = "red";
-                    break;
-
-                case RRDCALC_STATUS_WARNING:
-                    value_color = "orange";
-                    break;
-
-                case RRDCALC_STATUS_CLEAR:
-                    value_color = "brightgreen";
-                    break;
-
-                case RRDCALC_STATUS_UNDEFINED:
-                    value_color = "lightgrey";
-                    break;
-
-                case RRDCALC_STATUS_UNINITIALIZED:
-                    value_color = "#000";
-                    break;
-
-                default:
-                    value_color = "grey";
-                    break;
-            }
-        }
-
-        buffer_svg(w->response.data,
-                label,
-                (isnan(rc->value)||isinf(rc->value)) ? rc->value : rc->value * multiply / divide,
-                units,
-                label_color,
-                value_color,
-                precision);
-        ret = 200;
-    }
-    else {
-        time_t latest_timestamp = 0;
-        int value_is_null = 1;
-        calculated_number n = NAN;
-        ret = 500;
-
-        // if the collected value is too old, don't calculate its value
-        if (rrdset_last_entry_t(st) >= (now_realtime_sec() - (st->update_every * st->gap_when_lost_iterations_above)))
-            ret = rrd2value(st,
-                            w->response.data,
-                            &n,
-                            (dimensions) ? buffer_tostring(dimensions) : NULL,
-                            points,
-                            after,
-                            before,
-                            group,
-                            options,
-                            NULL,
-                            &latest_timestamp,
-                            &value_is_null);
-
-        // if the value cannot be calculated, show empty badge
-        if (ret != 200) {
-            buffer_no_cacheable(w->response.data);
-            value_is_null = 1;
-            n = 0;
-            ret = 200;
-        }
-        else if (refresh > 0) {
-            buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
-            w->response.data->expires = now_realtime_sec() + refresh;
-        }
-        else buffer_no_cacheable(w->response.data);
-
-        // render the badge
-        buffer_svg(w->response.data,
-                label,
-                (value_is_null)?NAN:(n * multiply / divide),
-                units,
-                label_color,
-                value_color,
-                precision);
-    }
-
-cleanup:
-    if(dimensions)
-        buffer_free(dimensions);
-    return ret;
-}
-
-// returns the HTTP code
-int web_client_api_request_v1_data(struct web_client *w, char *url)
-{
-    debug(D_WEB_CLIENT, "%llu: API v1 data with URL '%s'", w->id, url);
-
-    int ret = 400;
-    BUFFER *dimensions = NULL;
-
-    buffer_flush(w->response.data);
-
-    char    *google_version = "0.6",
-            *google_reqId = "0",
-            *google_sig = "0",
-            *google_out = "json",
-            *responseHandler = NULL,
-            *outFileName = NULL;
-
-    time_t last_timestamp_in_data = 0, google_timestamp = 0;
-
-    char *chart = NULL
-            , *before_str = NULL
-            , *after_str = NULL
-            , *points_str = NULL;
-
-    int group = GROUP_AVERAGE;
-    uint32_t format = DATASOURCE_JSON;
-    uint32_t options = 0x00000000;
-
-    while(url) {
-        char *value = mystrsep(&url, "?&");
-        if(!value || !*value) continue;
-
-        char *name = mystrsep(&value, "=");
-        if(!name || !*name) continue;
-        if(!value || !*value) continue;
-
-        debug(D_WEB_CLIENT, "%llu: API v1 data query param '%s' with value '%s'", w->id, name, value);
-
-        // name and value are now the parameters
-        // they are not null and not empty
-
-        if(!strcmp(name, "chart")) chart = value;
-        else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
-            if(!dimensions) dimensions = buffer_create(100);
-            buffer_strcat(dimensions, "|");
-            buffer_strcat(dimensions, value);
-        }
-        else if(!strcmp(name, "after")) after_str = value;
-        else if(!strcmp(name, "before")) before_str = value;
-        else if(!strcmp(name, "points")) points_str = value;
-        else if(!strcmp(name, "group")) {
-            group = web_client_api_request_v1_data_group(value, GROUP_AVERAGE);
-        }
-        else if(!strcmp(name, "format")) {
-            format = web_client_api_request_v1_data_format(value);
-        }
-        else if(!strcmp(name, "options")) {
-            options |= web_client_api_request_v1_data_options(value);
-        }
-        else if(!strcmp(name, "callback")) {
-            responseHandler = value;
-        }
-        else if(!strcmp(name, "filename")) {
-            outFileName = value;
-        }
-        else if(!strcmp(name, "tqx")) {
-            // parse Google Visualization API options
-            // https://developers.google.com/chart/interactive/docs/dev/implementing_data_source
-            char *tqx_name, *tqx_value;
-
-            while(value) {
-                tqx_value = mystrsep(&value, ";");
-                if(!tqx_value || !*tqx_value) continue;
-
-                tqx_name = mystrsep(&tqx_value, ":");
-                if(!tqx_name || !*tqx_name) continue;
-                if(!tqx_value || !*tqx_value) continue;
-
-                if(!strcmp(tqx_name, "version"))
-                    google_version = tqx_value;
-                else if(!strcmp(tqx_name, "reqId"))
-                    google_reqId = tqx_value;
-                else if(!strcmp(tqx_name, "sig")) {
-                    google_sig = tqx_value;
-                    google_timestamp = strtoul(google_sig, NULL, 0);
-                }
-                else if(!strcmp(tqx_name, "out")) {
-                    google_out = tqx_value;
-                    format = web_client_api_request_v1_data_google_format(google_out);
-                }
-                else if(!strcmp(tqx_name, "responseHandler"))
-                    responseHandler = tqx_value;
-                else if(!strcmp(tqx_name, "outFileName"))
-                    outFileName = tqx_value;
-            }
-        }
-    }
-
-    if(!chart || !*chart) {
-        buffer_sprintf(w->response.data, "No chart id is given at the request.");
-        goto cleanup;
-    }
-
-    RRDSET *st = rrdset_find(chart);
-    if(!st) st = rrdset_find_byname(chart);
-    if(!st) {
-        buffer_strcat(w->response.data, "Chart is not found: ");
-        buffer_strcat_htmlescape(w->response.data, chart);
-        ret = 404;
-        goto cleanup;
-    }
-
-    long long before = (before_str && *before_str)?str2l(before_str):0;
-    long long after  = (after_str  && *after_str) ?str2l(after_str):0;
-    int       points = (points_str && *points_str)?str2i(points_str):0;
-
-    debug(D_WEB_CLIENT, "%llu: API command 'data' for chart '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', format '%u', options '0x%08x'"
-            , w->id
-            , chart
-            , (dimensions)?buffer_tostring(dimensions):""
-            , after
-            , before
-            , points
-            , group
-            , format
-            , options
-            );
-
-    if(outFileName && *outFileName) {
-        buffer_sprintf(w->response.header, "Content-Disposition: attachment; filename=\"%s\"\r\n", outFileName);
-        debug(D_WEB_CLIENT, "%llu: generating outfilename header: '%s'", w->id, outFileName);
-    }
-
-    if(format == DATASOURCE_DATATABLE_JSONP) {
-        if(responseHandler == NULL)
-            responseHandler = "google.visualization.Query.setResponse";
-
-        debug(D_WEB_CLIENT_ACCESS, "%llu: GOOGLE JSON/JSONP: version = '%s', reqId = '%s', sig = '%s', out = '%s', responseHandler = '%s', outFileName = '%s'",
-                w->id, google_version, google_reqId, google_sig, google_out, responseHandler, outFileName
-            );
-
-        buffer_sprintf(w->response.data,
-            "%s({version:'%s',reqId:'%s',status:'ok',sig:'%ld',table:",
-            responseHandler, google_version, google_reqId, st->last_updated.tv_sec);
-    }
-    else if(format == DATASOURCE_JSONP) {
-        if(responseHandler == NULL)
-            responseHandler = "callback";
-
-        buffer_strcat(w->response.data, responseHandler);
-        buffer_strcat(w->response.data, "(");
-    }
-
-    ret = rrd2format(st, w->response.data, dimensions, format, points, after, before, group, options, &last_timestamp_in_data);
-
-    if(format == DATASOURCE_DATATABLE_JSONP) {
-        if(google_timestamp < last_timestamp_in_data)
-            buffer_strcat(w->response.data, "});");
-
-        else {
-            // the client already has the latest data
-            buffer_flush(w->response.data);
-            buffer_sprintf(w->response.data,
-                "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'not_modified',message:'Data not modified'}]});",
-                responseHandler, google_version, google_reqId);
-        }
-    }
-    else if(format == DATASOURCE_JSONP)
-        buffer_strcat(w->response.data, ");");
-
-cleanup:
-    if(dimensions) buffer_free(dimensions);
-    return ret;
-}
-
-
-int web_client_api_request_v1_registry(struct web_client *w, char *url)
-{
-    static uint32_t hash_action = 0, hash_access = 0, hash_hello = 0, hash_delete = 0, hash_search = 0,
-            hash_switch = 0, hash_machine = 0, hash_url = 0, hash_name = 0, hash_delete_url = 0, hash_for = 0,
-            hash_to = 0 /*, hash_redirects = 0 */;
-
-    if(unlikely(!hash_action)) {
-        hash_action = simple_hash("action");
-        hash_access = simple_hash("access");
-        hash_hello = simple_hash("hello");
-        hash_delete = simple_hash("delete");
-        hash_search = simple_hash("search");
-        hash_switch = simple_hash("switch");
-        hash_machine = simple_hash("machine");
-        hash_url = simple_hash("url");
-        hash_name = simple_hash("name");
-        hash_delete_url = simple_hash("delete_url");
-        hash_for = simple_hash("for");
-        hash_to = simple_hash("to");
-/*
-        hash_redirects = simple_hash("redirects");
-*/
-    }
-
-    char person_guid[GUID_LEN + 1] = "";
-
-    debug(D_WEB_CLIENT, "%llu: API v1 registry with URL '%s'", w->id, url);
-
-    // FIXME
-    // The browser may send multiple cookies with our id
-    
-    char *cookie = strstr(w->response.data->buffer, NETDATA_REGISTRY_COOKIE_NAME "=");
-    if(cookie)
-        strncpyz(person_guid, &cookie[sizeof(NETDATA_REGISTRY_COOKIE_NAME)], 36);
-
-    char action = '\0';
-    char *machine_guid = NULL,
-            *machine_url = NULL,
-            *url_name = NULL,
-            *search_machine_guid = NULL,
-            *delete_url = NULL,
-            *to_person_guid = NULL;
-/*
-    int redirects = 0;
-*/
-
-    while(url) {
-        char *value = mystrsep(&url, "?&");
-        if (!value || !*value) continue;
-
-        char *name = mystrsep(&value, "=");
-        if (!name || !*name) continue;
-        if (!value || !*value) continue;
-
-        debug(D_WEB_CLIENT, "%llu: API v1 registry query param '%s' with value '%s'", w->id, name, value);
-
-        uint32_t hash = simple_hash(name);
-
-        if(hash == hash_action && !strcmp(name, "action")) {
-            uint32_t vhash = simple_hash(value);
-
-            if(vhash == hash_access && !strcmp(value, "access")) action = 'A';
-            else if(vhash == hash_hello && !strcmp(value, "hello")) action = 'H';
-            else if(vhash == hash_delete && !strcmp(value, "delete")) action = 'D';
-            else if(vhash == hash_search && !strcmp(value, "search")) action = 'S';
-            else if(vhash == hash_switch && !strcmp(value, "switch")) action = 'W';
-#ifdef NETDATA_INTERNAL_CHECKS
-            else error("unknown registry action '%s'", value);
-#endif /* NETDATA_INTERNAL_CHECKS */
-        }
-/*
-        else if(hash == hash_redirects && !strcmp(name, "redirects"))
-            redirects = atoi(value);
-*/
-        else if(hash == hash_machine && !strcmp(name, "machine"))
-            machine_guid = value;
-
-        else if(hash == hash_url && !strcmp(name, "url"))
-            machine_url = value;
-
-        else if(action == 'A') {
-            if(hash == hash_name && !strcmp(name, "name"))
-                url_name = value;
-        }
-        else if(action == 'D') {
-            if(hash == hash_delete_url && !strcmp(name, "delete_url"))
-                delete_url = value;
-        }
-        else if(action == 'S') {
-            if(hash == hash_for && !strcmp(name, "for"))
-                search_machine_guid = value;
-        }
-        else if(action == 'W') {
-            if(hash == hash_to && !strcmp(name, "to"))
-                to_person_guid = value;
-        }
-#ifdef NETDATA_INTERNAL_CHECKS
-        else error("unused registry URL parameter '%s' with value '%s'", name, value);
-#endif /* NETDATA_INTERNAL_CHECKS */
-    }
-
-    if(respect_web_browser_do_not_track_policy && w->donottrack) {
-        buffer_flush(w->response.data);
-        buffer_sprintf(w->response.data, "Your web browser is sending 'DNT: 1' (Do Not Track). The registry requires persistent cookies on your browser to work.");
-        return 400;
-    }
-
-    if(action == 'A' && (!machine_guid || !machine_url || !url_name)) {
-        error("Invalid registry request - access requires these parameters: machine ('%s'), url ('%s'), name ('%s')",
-                machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", url_name?url_name:"UNSET");
-        buffer_flush(w->response.data);
-        buffer_strcat(w->response.data, "Invalid registry Access request.");
-        return 400;
-    }
-    else if(action == 'D' && (!machine_guid || !machine_url || !delete_url)) {
-        error("Invalid registry request - delete requires these parameters: machine ('%s'), url ('%s'), delete_url ('%s')",
-                machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", delete_url?delete_url:"UNSET");
-        buffer_flush(w->response.data);
-        buffer_strcat(w->response.data, "Invalid registry Delete request.");
-        return 400;
-    }
-    else if(action == 'S' && (!machine_guid || !machine_url || !search_machine_guid)) {
-        error("Invalid registry request - search requires these parameters: machine ('%s'), url ('%s'), for ('%s')",
-                machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", search_machine_guid?search_machine_guid:"UNSET");
-        buffer_flush(w->response.data);
-        buffer_strcat(w->response.data, "Invalid registry Search request.");
-        return 400;
-    }
-    else if(action == 'W' && (!machine_guid || !machine_url || !to_person_guid)) {
-        error("Invalid registry request - switching identity requires these parameters: machine ('%s'), url ('%s'), to ('%s')",
-                machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", to_person_guid?to_person_guid:"UNSET");
+static inline int check_host_and_call(RRDHOST *host, struct web_client *w, char *url, int (*func)(RRDHOST *, struct web_client *, char *)) {
+    if(unlikely(host->rrd_memory_mode == RRD_MEMORY_MODE_NONE)) {
         buffer_flush(w->response.data);
-        buffer_strcat(w->response.data, "Invalid registry Switch request.");
+        buffer_strcat(w->response.data, "This host does not maintain a database");
         return 400;
     }
 
-    switch(action) {
-        case 'A':
-            w->tracking_required = 1;
-            return registry_request_access_json(w, person_guid, machine_guid, machine_url, url_name, now_realtime_sec());
-
-        case 'D':
-            w->tracking_required = 1;
-            return registry_request_delete_json(w, person_guid, machine_guid, machine_url, delete_url, now_realtime_sec());
-
-        case 'S':
-            w->tracking_required = 1;
-            return registry_request_search_json(w, person_guid, machine_guid, machine_url, search_machine_guid, now_realtime_sec());
-
-        case 'W':
-            w->tracking_required = 1;
-            return registry_request_switch_json(w, person_guid, machine_guid, machine_url, to_person_guid, now_realtime_sec());
-
-        case 'H':
-            return registry_request_hello_json(w);
-
-        default:
-            buffer_flush(w->response.data);
-            buffer_strcat(w->response.data, "Invalid registry request - you need to set an action: hello, access, delete, search");
-            return 400;
-    }
-}
-
-int web_client_api_request_v1(struct web_client *w, char *url) {
-    static uint32_t hash_data = 0, hash_chart = 0, hash_charts = 0, hash_registry = 0, hash_badge = 0, hash_alarms = 0, hash_alarm_log = 0, hash_alarm_variables = 0, hash_raw = 0;
-
-    if(unlikely(hash_data == 0)) {
-        hash_data = simple_hash("data");
-        hash_chart = simple_hash("chart");
-        hash_charts = simple_hash("charts");
-        hash_registry = simple_hash("registry");
-        hash_badge = simple_hash("badge.svg");
-        hash_alarms = simple_hash("alarms");
-        hash_alarm_log = simple_hash("alarm_log");
-        hash_alarm_variables = simple_hash("alarm_variables");
-        hash_raw = simple_hash("allmetrics");
-    }
-
-    // get the command
-    char *tok = mystrsep(&url, "/?&");
-    if(tok && *tok) {
-        debug(D_WEB_CLIENT, "%llu: Searching for API v1 command '%s'.", w->id, tok);
-        uint32_t hash = simple_hash(tok);
-
-        if(hash == hash_data && !strcmp(tok, "data"))
-            return web_client_api_request_v1_data(w, url);
-
-        else if(hash == hash_chart && !strcmp(tok, "chart"))
-            return web_client_api_request_v1_chart(w, url);
-
-        else if(hash == hash_charts && !strcmp(tok, "charts"))
-            return web_client_api_request_v1_charts(w, url);
-
-        else if(hash == hash_registry && !strcmp(tok, "registry"))
-            return web_client_api_request_v1_registry(w, url);
-
-        else if(hash == hash_badge && !strcmp(tok, "badge.svg"))
-            return web_client_api_request_v1_badge(w, url);
-
-        else if(hash == hash_alarms && !strcmp(tok, "alarms"))
-            return web_client_api_request_v1_alarms(w, url);
-
-        else if(hash == hash_alarm_log && !strcmp(tok, "alarm_log"))
-            return web_client_api_request_v1_alarm_log(w, url);
-
-        else if(hash == hash_alarm_variables && !strcmp(tok, "alarm_variables"))
-            return web_client_api_request_v1_alarm_variables(w, url);
-
-        else if(hash == hash_raw && !strcmp(tok, "allmetrics"))
-            return web_client_api_request_v1_allmetrics(w, url);
-
-        else {
-            buffer_flush(w->response.data);
-            buffer_strcat(w->response.data, "Unsupported v1 API command: ");
-            buffer_strcat_htmlescape(w->response.data, tok);
-            return 404;
-        }
-    }
-    else {
-        buffer_flush(w->response.data);
-        buffer_sprintf(w->response.data, "Which API v1 command?");
-        return 400;
-    }
+    return func(host, w, url);
 }
 
-int web_client_api_request(struct web_client *w, char *url)
+int web_client_api_request(RRDHOST *host, struct web_client *w, char *url)
 {
     // get the api version
     char *tok = mystrsep(&url, "/?&");
     if(tok && *tok) {
         debug(D_WEB_CLIENT, "%llu: Searching for API version '%s'.", w->id, tok);
         if(strcmp(tok, "v1") == 0)
-            return web_client_api_request_v1(w, url);
+            return web_client_api_request_v1(host, w, url);
         else {
             buffer_flush(w->response.data);
+            w->response.data->contenttype = CT_TEXT_HTML;
             buffer_strcat(w->response.data, "Unsupported API version: ");
             buffer_strcat_htmlescape(w->response.data, tok);
             return 404;
@@ -1494,179 +596,6 @@ int web_client_api_request(struct web_client *w, char *url)
     }
 }
 
-int web_client_api_old_data_request(struct web_client *w, char *url, int datasource_type)
-{
-    if(!url || !*url) {
-        buffer_flush(w->response.data);
-        buffer_sprintf(w->response.data, "Incomplete request.");
-        return 400;
-    }
-
-    RRDSET *st = NULL;
-
-    char *args = strchr(url, '?');
-    if(args) {
-        *args='\0';
-        args = &args[1];
-    }
-
-    // get the name of the data to show
-    char *tok = mystrsep(&url, "/");
-    if(!tok) tok = "";
-
-    // do we have such a data set?
-    if(*tok) {
-        debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
-        st = rrdset_find_byname(tok);
-        if(!st) st = rrdset_find(tok);
-    }
-
-    if(!st) {
-        // we don't have it
-        // try to send a file with that name
-        buffer_flush(w->response.data);
-        return(mysendfile(w, tok));
-    }
-
-    // we have it
-    debug(D_WEB_CLIENT, "%llu: Found RRD data with name '%s'.", w->id, tok);
-
-    // how many entries does the client want?
-    int lines = rrd_default_history_entries;
-    int group_count = 1;
-    time_t after = 0, before = 0;
-    int group_method = GROUP_AVERAGE;
-    int nonzero = 0;
-
-    if(url) {
-        // parse the lines required
-        tok = mystrsep(&url, "/");
-        if(tok) lines = str2i(tok);
-        if(lines < 1) lines = 1;
-    }
-    if(url) {
-        // parse the group count required
-        tok = mystrsep(&url, "/");
-        if(tok && *tok) group_count = str2i(tok);
-        if(group_count < 1) group_count = 1;
-        //if(group_count > save_history / 20) group_count = save_history / 20;
-    }
-    if(url) {
-        // parse the grouping method required
-        tok = mystrsep(&url, "/");
-        if(tok && *tok) {
-            if(strcmp(tok, "max") == 0) group_method = GROUP_MAX;
-            else if(strcmp(tok, "average") == 0) group_method = GROUP_AVERAGE;
-            else if(strcmp(tok, "sum") == 0) group_method = GROUP_SUM;
-            else debug(D_WEB_CLIENT, "%llu: Unknown group method '%s'", w->id, tok);
-        }
-    }
-    if(url) {
-        // parse after time
-        tok = mystrsep(&url, "/");
-        if(tok && *tok) after = str2ul(tok);
-        if(after < 0) after = 0;
-    }
-    if(url) {
-        // parse before time
-        tok = mystrsep(&url, "/");
-        if(tok && *tok) before = str2ul(tok);
-        if(before < 0) before = 0;
-    }
-    if(url) {
-        // parse nonzero
-        tok = mystrsep(&url, "/");
-        if(tok && *tok && strcmp(tok, "nonzero") == 0) nonzero = 1;
-    }
-
-    w->response.data->contenttype = CT_APPLICATION_JSON;
-    buffer_flush(w->response.data);
-
-    char *google_version = "0.6";
-    char *google_reqId = "0";
-    char *google_sig = "0";
-    char *google_out = "json";
-    char *google_responseHandler = "google.visualization.Query.setResponse";
-    char *google_outFileName = NULL;
-    time_t last_timestamp_in_data = 0;
-    if(datasource_type == DATASOURCE_DATATABLE_JSON || datasource_type == DATASOURCE_DATATABLE_JSONP) {
-
-        w->response.data->contenttype = CT_APPLICATION_X_JAVASCRIPT;
-
-        while(args) {
-            tok = mystrsep(&args, "&");
-            if(tok && *tok) {
-                char *name = mystrsep(&tok, "=");
-                if(name && *name && strcmp(name, "tqx") == 0) {
-                    char *key = mystrsep(&tok, ":");
-                    char *value = mystrsep(&tok, ";");
-                    if(key && value && *key && *value) {
-                        if(strcmp(key, "version") == 0)
-                            google_version = value;
-
-                        else if(strcmp(key, "reqId") == 0)
-                            google_reqId = value;
-
-                        else if(strcmp(key, "sig") == 0)
-                            google_sig = value;
-
-                        else if(strcmp(key, "out") == 0)
-                            google_out = value;
-
-                        else if(strcmp(key, "responseHandler") == 0)
-                            google_responseHandler = value;
-
-                        else if(strcmp(key, "outFileName") == 0)
-                            google_outFileName = value;
-                    }
-                }
-            }
-        }
-
-        debug(D_WEB_CLIENT_ACCESS, "%llu: GOOGLE JSONP: version = '%s', reqId = '%s', sig = '%s', out = '%s', responseHandler = '%s', outFileName = '%s'",
-            w->id, google_version, google_reqId, google_sig, google_out, google_responseHandler, google_outFileName
-            );
-
-        if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
-            last_timestamp_in_data = strtoul(google_sig, NULL, 0);
-
-            // check the client wants json
-            if(strcmp(google_out, "json") != 0) {
-                buffer_sprintf(w->response.data,
-                    "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'invalid_query',message:'output format is not supported',detailed_message:'the format %s requested is not supported by netdata.'}]});",
-                    google_responseHandler, google_version, google_reqId, google_out);
-                    return 200;
-            }
-        }
-    }
-
-    if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
-        buffer_sprintf(w->response.data,
-            "%s({version:'%s',reqId:'%s',status:'ok',sig:'%ld',table:",
-            google_responseHandler, google_version, google_reqId, st->last_updated.tv_sec);
-    }
-
-    debug(D_WEB_CLIENT_ACCESS, "%llu: Sending RRD data '%s' (id %s, %d lines, %d group, %d group_method, %ld after, %ld before).",
-        w->id, st->name, st->id, lines, group_count, group_method, after, before);
-
-    time_t timestamp_in_data = rrd_stats_json(datasource_type, st, w->response.data, lines, group_count, group_method, (unsigned long)after, (unsigned long)before, nonzero);
-
-    if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
-        if(timestamp_in_data > last_timestamp_in_data)
-            buffer_strcat(w->response.data, "});");
-
-        else {
-            // the client already has the latest data
-            buffer_flush(w->response.data);
-            buffer_sprintf(w->response.data,
-                "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'not_modified',message:'Data not modified'}]});",
-                google_responseHandler, google_version, google_reqId);
-        }
-    }
-
-    return 200;
-}
-
 const char *web_content_type_to_string(uint8_t contenttype) {
     switch(contenttype) {
         case CT_TEXT_HTML:
@@ -1853,7 +782,13 @@ static inline char *http_header_parse(struct web_client *w, char *s) {
 // > 0 : request is not supported
 // < 0 : request is incomplete - wait for more data
 
-static inline int http_request_validate(struct web_client *w) {
+typedef enum http_validation {
+    HTTP_VALIDATION_OK,
+    HTTP_VALIDATION_NOT_SUPPORTED,
+    HTTP_VALIDATION_INCOMPLETE
+} HTTP_VALIDATION;
+
+static inline HTTP_VALIDATION http_request_validate(struct web_client *w) {
     char *s = w->response.data->buffer, *encoded_url = NULL;
 
     // is is a valid request?
@@ -1865,9 +800,13 @@ static inline int http_request_validate(struct web_client *w) {
         encoded_url = s = &s[8];
         w->mode = WEB_CLIENT_MODE_OPTIONS;
     }
+    else if(!strncmp(s, "STREAM ", 7)) {
+        encoded_url = s = &s[7];
+        w->mode = WEB_CLIENT_MODE_STREAM;
+    }
     else {
         w->wait_receive = 0;
-        return 1;
+        return HTTP_VALIDATION_NOT_SUPPORTED;
     }
 
     // find the SPACE + "HTTP/"
@@ -1883,7 +822,7 @@ static inline int http_request_validate(struct web_client *w) {
     // incomplete requests
     if(unlikely(!*s)) {
         w->wait_receive = 1;
-        return -2;
+        return HTTP_VALIDATION_INCOMPLETE;
     }
 
     // we have the end of encoded_url - remember it
@@ -1914,7 +853,7 @@ static inline int http_request_validate(struct web_client *w) {
                 strncpyz(w->last_url, w->decoded_url, URL_MAX);
 
                 w->wait_receive = 0;
-                return 0;
+                return HTTP_VALIDATION_OK;
             }
 
             // another header line
@@ -1924,7 +863,7 @@ static inline int http_request_validate(struct web_client *w) {
 
     // incomplete request
     w->wait_receive = 1;
-    return -3;
+    return HTTP_VALIDATION_INCOMPLETE;
 }
 
 static inline void web_client_send_http_header(struct web_client *w) {
@@ -1934,7 +873,7 @@ static inline void web_client_send_http_header(struct web_client *w) {
     // set a proper expiration date, if not already set
     if(unlikely(!w->response.data->expires)) {
         if(w->response.data->options & WB_CONTENT_NO_CACHEABLE)
-            w->response.data->expires = w->tv_ready.tv_sec + rrd_update_every;
+            w->response.data->expires = w->tv_ready.tv_sec + localhost->rrd_update_every;
         else
             w->response.data->expires = w->tv_ready.tv_sec + 86400;
     }
@@ -2069,7 +1008,46 @@ static inline void web_client_send_http_header(struct web_client *w) {
         w->stats_sent_bytes += bytes;
 }
 
-void web_client_process(struct web_client *w) {
+static inline int web_client_process_url(RRDHOST *host, struct web_client *w, char *url);
+
+static inline int web_client_switch_host(RRDHOST *host, struct web_client *w, char *url) {
+    static uint32_t hash_localhost = 0;
+
+    if(unlikely(!hash_localhost)) {
+        hash_localhost = simple_hash("localhost");
+    }
+
+    if(host != localhost) {
+        buffer_flush(w->response.data);
+        buffer_strcat(w->response.data, "Nesting of hosts is not allowed.");
+        return 400;
+    }
+
+    char *tok = mystrsep(&url, "/?&");
+    if(tok && *tok) {
+        debug(D_WEB_CLIENT, "%llu: Searching for host with name '%s'.", w->id, tok);
+
+        // copy the URL, we need it to serve files
+        w->last_url[0] = '/';
+        if(url && *url) strncpyz(&w->last_url[1], url, URL_MAX - 1);
+        else w->last_url[1] = '\0';
+
+        uint32_t hash = simple_hash(tok);
+
+        host = rrdhost_find_by_hostname(tok, hash);
+        if(!host) host = rrdhost_find_by_guid(tok, hash);
+
+        if(host) return web_client_process_url(host, w, url);
+    }
+
+    buffer_flush(w->response.data);
+    w->response.data->contenttype = CT_TEXT_HTML;
+    buffer_strcat(w->response.data, "This netdata does not maintain a database for host: ");
+    buffer_strcat_htmlescape(w->response.data, tok?tok:"");
+    return 404;
+}
+
+static inline int web_client_process_url(RRDHOST *host, struct web_client *w, char *url) {
     static uint32_t
             hash_api = 0,
             hash_netdata_conf = 0,
@@ -2077,15 +1055,13 @@ void web_client_process(struct web_client *w) {
             hash_datasource = 0,
             hash_graph = 0,
             hash_list = 0,
-            hash_all_json = 0;
+            hash_all_json = 0,
+            hash_host = 0;
 
 #ifdef NETDATA_INTERNAL_CHECKS
     static uint32_t hash_exit = 0, hash_debug = 0, hash_mirror = 0;
 #endif
 
-    // start timing us
-    now_realtime_timeval(&w->tv_in);
-
     if(unlikely(!hash_api)) {
         hash_api = simple_hash("api");
         hash_netdata_conf = simple_hash("netdata.conf");
@@ -2094,6 +1070,7 @@ void web_client_process(struct web_client *w) {
         hash_graph = simple_hash(WEB_PATH_GRAPH);
         hash_list = simple_hash("list");
         hash_all_json = simple_hash("all.json");
+        hash_host = simple_hash("host");
 #ifdef NETDATA_INTERNAL_CHECKS
         hash_exit = simple_hash("exit");
         hash_debug = simple_hash("debug");
@@ -2101,202 +1078,174 @@ void web_client_process(struct web_client *w) {
 #endif
     }
 
-    int code = 500;
-
-    int what_to_do = http_request_validate(w);
-
-    // wait for more data
-    if(what_to_do < 0) {
-        if(w->response.data->len > TOO_BIG_REQUEST) {
-            strcpy(w->last_url, "too big request");
-
-            debug(D_WEB_CLIENT_ACCESS, "%llu: Received request is too big (%zu bytes).", w->id, w->response.data->len);
+    char *tok = mystrsep(&url, "/?");
+    if(likely(tok && *tok)) {
+        uint32_t hash = simple_hash(tok);
+        debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
 
-            code = 400;
+        if(unlikely(hash == hash_api && strcmp(tok, "api") == 0)) {                           // current API
+            debug(D_WEB_CLIENT_ACCESS, "%llu: API request ...", w->id);
+            return check_host_and_call(host, w, url, web_client_api_request);
+        }
+        else if(unlikely(hash == hash_host && strcmp(tok, "host") == 0)) {                    // host switching
+            debug(D_WEB_CLIENT_ACCESS, "%llu: host switch request ...", w->id);
+            return web_client_switch_host(host, w, url);
+        }
+        else if(unlikely(hash == hash_data && strcmp(tok, WEB_PATH_DATA) == 0)) {             // old API "data"
+            debug(D_WEB_CLIENT_ACCESS, "%llu: old API data request...", w->id);
+            return check_host_and_call(host, w, url, web_client_api_old_data_request_json);
+        }
+        else if(unlikely(hash == hash_datasource && strcmp(tok, WEB_PATH_DATASOURCE) == 0)) { // old API "datasource"
+            debug(D_WEB_CLIENT_ACCESS, "%llu: old API datasource request...", w->id);
+            return check_host_and_call(host, w, url, web_client_api_old_data_request_jsonp);
+        }
+        else if(unlikely(hash == hash_graph && strcmp(tok, WEB_PATH_GRAPH) == 0)) {           // old API "graph"
+            debug(D_WEB_CLIENT_ACCESS, "%llu: old API graph request...", w->id);
+            return check_host_and_call(host, w, url, web_client_api_old_graph_request);
+        }
+        else if(unlikely(hash == hash_list && strcmp(tok, "list") == 0)) {                    // old API "list"
+            debug(D_WEB_CLIENT_ACCESS, "%llu: old API list request...", w->id);
+            return check_host_and_call(host, w, url, web_client_api_old_list_request);
+        }
+        else if(unlikely(hash == hash_all_json && strcmp(tok, "all.json") == 0)) {            // old API "all.json"
+            debug(D_WEB_CLIENT_ACCESS, "%llu: old API all.json request...", w->id);
+            return check_host_and_call(host, w, url, web_client_api_old_all_json);
+        }
+        else if(unlikely(hash == hash_netdata_conf && strcmp(tok, "netdata.conf") == 0)) {    // netdata.conf
+            debug(D_WEB_CLIENT_ACCESS, "%llu: generating netdata.conf ...", w->id);
+            w->response.data->contenttype = CT_TEXT_PLAIN;
             buffer_flush(w->response.data);
-            buffer_sprintf(w->response.data, "Received request is too big  (%zu bytes).\r\n", w->response.data->len);
+            config_generate(w->response.data, 0);
+            return 200;
         }
-        else {
-            // wait for more data
-            return;
+#ifdef NETDATA_INTERNAL_CHECKS
+        else if(unlikely(hash == hash_exit && strcmp(tok, "exit") == 0)) {
+            w->response.data->contenttype = CT_TEXT_PLAIN;
+            buffer_flush(w->response.data);
+
+            if(!netdata_exit)
+                buffer_strcat(w->response.data, "ok, will do...");
+            else
+                buffer_strcat(w->response.data, "I am doing it already");
+
+            error("web request to exit received.");
+            netdata_cleanup_and_exit(0);
+            return 200;
         }
-    }
-    else if(what_to_do > 0) {
-        // strcpy(w->last_url, "not a valid request");
+        else if(unlikely(hash == hash_debug && strcmp(tok, "debug") == 0)) {
+            buffer_flush(w->response.data);
 
-        debug(D_WEB_CLIENT_ACCESS, "%llu: Cannot understand '%s'.", w->id, w->response.data->buffer);
+            // get the name of the data to show
+            tok = mystrsep(&url, "/?&");
+            if(tok && *tok) {
+                debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
+
+                // do we have such a data set?
+                RRDSET *st = rrdset_find_byname(host, tok);
+                if(!st) st = rrdset_find(host, tok);
+                if(!st) {
+                    w->response.data->contenttype = CT_TEXT_HTML;
+                    buffer_strcat(w->response.data, "Chart is not found: ");
+                    buffer_strcat_htmlescape(w->response.data, tok);
+                    debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok);
+                    return 404;
+                }
+
+                debug_flags |= D_RRD_STATS;
+
+                if(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))
+                    rrdset_flag_clear(st, RRDSET_FLAG_DEBUG);
+                else
+                    rrdset_flag_set(st, RRDSET_FLAG_DEBUG);
+
+                w->response.data->contenttype = CT_TEXT_HTML;
+                buffer_sprintf(w->response.data, "Chart has now debug %s: ", rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?"enabled":"disabled");
+                buffer_strcat_htmlescape(w->response.data, tok);
+                debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?"enabled":"disabled");
+                return 200;
+            }
 
-        code = 500;
-        buffer_flush(w->response.data);
-        buffer_strcat(w->response.data, "I don't understand you...\r\n");
-    }
-    else { // what_to_do == 0
-        if(w->mode == WEB_CLIENT_MODE_OPTIONS) {
-            code = 200;
-            w->response.data->contenttype = CT_TEXT_PLAIN;
             buffer_flush(w->response.data);
-            buffer_strcat(w->response.data, "OK");
+            buffer_strcat(w->response.data, "debug which chart?\r\n");
+            return 400;
         }
-        else {
-            char *url = w->decoded_url;
-            char *tok = mystrsep(&url, "/?");
-            if(tok && *tok) {
-                uint32_t hash = simple_hash(tok);
-                debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
+        else if(unlikely(hash == hash_mirror && strcmp(tok, "mirror") == 0)) {
+            debug(D_WEB_CLIENT_ACCESS, "%llu: Mirroring...", w->id);
 
-                if(hash == hash_api && strcmp(tok, "api") == 0) {
-                    // the client is requesting api access
-                    code = web_client_api_request(w, url);
-                }
-                else if(hash == hash_netdata_conf && strcmp(tok, "netdata.conf") == 0) {
-                    code = 200;
-                    debug(D_WEB_CLIENT_ACCESS, "%llu: Sending netdata.conf ...", w->id);
+            // replace the zero bytes with spaces
+            buffer_char_replace(w->response.data, '\0', ' ');
 
-                    w->response.data->contenttype = CT_TEXT_PLAIN;
-                    buffer_flush(w->response.data);
-                    generate_config(w->response.data, 0);
-                }
-                else if(hash == hash_data && strcmp(tok, WEB_PATH_DATA) == 0) { // "data"
-                    // the client is requesting rrd data -- OLD API
-                    code = web_client_api_old_data_request(w, url, DATASOURCE_JSON);
-                }
-                else if(hash == hash_datasource && strcmp(tok, WEB_PATH_DATASOURCE) == 0) { // "datasource"
-                    // the client is requesting google datasource -- OLD API
-                    code = web_client_api_old_data_request(w, url, DATASOURCE_DATATABLE_JSONP);
-                }
-                else if(hash == hash_graph && strcmp(tok, WEB_PATH_GRAPH) == 0) { // "graph"
-                    // the client is requesting an rrd graph -- OLD API
-
-                    // get the name of the data to show
-                    tok = mystrsep(&url, "/?&");
-                    if(tok && *tok) {
-                        debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
-
-                        // do we have such a data set?
-                        RRDSET *st = rrdset_find_byname(tok);
-                        if(!st) st = rrdset_find(tok);
-                        if(!st) {
-                            // we don't have it
-                            // try to send a file with that name
-                            buffer_flush(w->response.data);
-                            code = mysendfile(w, tok);
-                        }
-                        else {
-                            code = 200;
-                            debug(D_WEB_CLIENT_ACCESS, "%llu: Sending %s.json of RRD_STATS...", w->id, st->name);
-                            w->response.data->contenttype = CT_APPLICATION_JSON;
-                            buffer_flush(w->response.data);
-                            rrd_stats_graph_json(st, url, w->response.data);
-                        }
-                    }
-                    else {
-                        code = 400;
-                        buffer_flush(w->response.data);
-                        buffer_strcat(w->response.data, "Graph name?\r\n");
-                    }
-                }
-                else if(hash == hash_list && strcmp(tok, "list") == 0) {
-                    // OLD API
-                    code = 200;
+            // just leave the buffer as is
+            // it will be copied back to the client
 
-                    debug(D_WEB_CLIENT_ACCESS, "%llu: Sending list of RRD_STATS...", w->id);
+            return 200;
+        }
+#endif  /* NETDATA_INTERNAL_CHECKS */
+    }
 
-                    buffer_flush(w->response.data);
-                    RRDSET *st = localhost.rrdset_root;
+    char filename[FILENAME_MAX+1];
+    url = filename;
+    strncpyz(filename, w->last_url, FILENAME_MAX);
+    tok = mystrsep(&url, "?");
+    buffer_flush(w->response.data);
+    return mysendfile(w, (tok && *tok)?tok:"/");
+}
 
-                    for ( ; st ; st = st->next )
-                        buffer_sprintf(w->response.data, "%s\n", st->name);
-                }
-                else if(hash == hash_all_json && strcmp(tok, "all.json") == 0) {
-                    // OLD API
-                    code = 200;
-                    debug(D_WEB_CLIENT_ACCESS, "%llu: Sending JSON list of all monitors of RRD_STATS...", w->id);
+void web_client_process_request(struct web_client *w) {
 
-                    w->response.data->contenttype = CT_APPLICATION_JSON;
-                    buffer_flush(w->response.data);
-                    rrd_stats_all_json(w->response.data);
-                }
-#ifdef NETDATA_INTERNAL_CHECKS
-                else if(hash == hash_exit && strcmp(tok, "exit") == 0) {
-                    code = 200;
-                    w->response.data->contenttype = CT_TEXT_PLAIN;
-                    buffer_flush(w->response.data);
+    // start timing us
+    now_realtime_timeval(&w->tv_in);
 
-                    if(!netdata_exit)
-                        buffer_strcat(w->response.data, "ok, will do...");
-                    else
-                        buffer_strcat(w->response.data, "I am doing it already");
+    switch(http_request_validate(w)) {
+        case HTTP_VALIDATION_OK:
+            switch(w->mode) {
+                case WEB_CLIENT_MODE_STREAM:
+                    w->response.code = rrdpush_receiver_thread_spawn(localhost, w, w->decoded_url);
+                    return;
 
-                    error("web request to exit received.");
-                    netdata_cleanup_and_exit(0);
-                }
-                else if(hash == hash_debug && strcmp(tok, "debug") == 0) {
+                case WEB_CLIENT_MODE_OPTIONS:
+                    w->response.data->contenttype = CT_TEXT_PLAIN;
                     buffer_flush(w->response.data);
+                    buffer_strcat(w->response.data, "OK");
+                    w->response.code = 200;
+                    break;
 
-                    // get the name of the data to show
-                    tok = mystrsep(&url, "/?&");
-                    if(tok && *tok) {
-                        debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
-
-                        // do we have such a data set?
-                        RRDSET *st = rrdset_find_byname(tok);
-                        if(!st) st = rrdset_find(tok);
-                        if(!st) {
-                            code = 404;
-                            buffer_strcat(w->response.data, "Chart is not found: ");
-                            buffer_strcat_htmlescape(w->response.data, tok);
-                            debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok);
-                        }
-                        else {
-                            code = 200;
-                            debug_flags |= D_RRD_STATS;
-                            st->debug = !st->debug;
-                            buffer_sprintf(w->response.data, "Chart has now debug %s: ", st->debug?"enabled":"disabled");
-                            buffer_strcat_htmlescape(w->response.data, tok);
-                            debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, st->debug?"enabled":"disabled");
-                        }
-                    }
-                    else {
-                        code = 500;
-                        buffer_flush(w->response.data);
-                        buffer_strcat(w->response.data, "debug which chart?\r\n");
-                    }
-                }
-                else if(hash == hash_mirror && strcmp(tok, "mirror") == 0) {
-                    code = 200;
+                case WEB_CLIENT_MODE_FILECOPY:
+                case WEB_CLIENT_MODE_NORMAL:
+                    w->response.code = web_client_process_url(localhost, w, w->decoded_url);
+                    break;
+            }
+            break;
 
-                    debug(D_WEB_CLIENT_ACCESS, "%llu: Mirroring...", w->id);
+        case HTTP_VALIDATION_INCOMPLETE:
+            if(w->response.data->len > TOO_BIG_REQUEST) {
+                strcpy(w->last_url, "too big request");
 
-                    // replace the zero bytes with spaces
-                    buffer_char_replace(w->response.data, '\0', ' ');
+                debug(D_WEB_CLIENT_ACCESS, "%llu: Received request is too big (%zu bytes).", w->id, w->response.data->len);
 
-                    // just leave the buffer as is
-                    // it will be copied back to the client
-                }
-#endif  /* NETDATA_INTERNAL_CHECKS */
-                else {
-                    char filename[FILENAME_MAX+1];
-                    url = filename;
-                    strncpyz(filename, w->last_url, FILENAME_MAX);
-                    tok = mystrsep(&url, "?");
-                    buffer_flush(w->response.data);
-                    code = mysendfile(w, (tok && *tok)?tok:"/");
-                }
+                buffer_flush(w->response.data);
+                buffer_sprintf(w->response.data, "Received request is too big  (%zu bytes).\r\n", w->response.data->len);
+                w->response.code = 400;
             }
             else {
-                char filename[FILENAME_MAX+1];
-                url = filename;
-                strncpyz(filename, w->last_url, FILENAME_MAX);
-                tok = mystrsep(&url, "?");
-                buffer_flush(w->response.data);
-                code = mysendfile(w, (tok && *tok)?tok:"/");
+                // wait for more data
+                return;
             }
-        }
+            break;
+
+        case HTTP_VALIDATION_NOT_SUPPORTED:
+            debug(D_WEB_CLIENT_ACCESS, "%llu: Cannot understand '%s'.", w->id, w->response.data->buffer);
+
+            buffer_flush(w->response.data);
+            buffer_strcat(w->response.data, "I don't understand you...\r\n");
+            w->response.code = 400;
+            break;
     }
 
+    // keep track of the time we done processing
     now_realtime_timeval(&w->tv_ready);
+
     w->response.sent = 0;
-    w->response.code = code;
 
     // set a proper last modified date
     if(unlikely(!w->response.data->date))
@@ -2308,8 +1257,11 @@ void web_client_process(struct web_client *w) {
     if(w->response.data->len) w->wait_send = 1;
     else w->wait_send = 0;
 
-    // pretty logging
     switch(w->mode) {
+        case WEB_CLIENT_MODE_STREAM:
+            debug(D_WEB_CLIENT, "%llu: STREAM done.", w->id);
+            break;
+
         case WEB_CLIENT_MODE_OPTIONS:
             debug(D_WEB_CLIENT, "%llu: Done preparing the OPTIONS response. Sending data (%zu bytes) to client.", w->id, w->response.data->len);
             break;
@@ -2341,7 +1293,7 @@ void web_client_process(struct web_client *w) {
             break;
 
         default:
-            fatal("%llu: Unknown client mode %d.", w->id, w->mode);
+            fatal("%llu: Unknown client mode %u.", w->id, w->mode);
             break;
     }
 }
@@ -2663,11 +1615,14 @@ void *web_client_main(void *ptr)
 
     struct web_client *w = ptr;
     struct pollfd fds[2], *ifd, *ofd;
-    int retval, fdmax = 0, timeout;
+    int retval, timeout;
+    nfds_t fdmax = 0;
 
     log_access("%llu: %s port %s connected on thread task id %d", w->id, w->client_ip, w->client_port, gettid());
 
     for(;;) {
+        if(unlikely(netdata_exit)) break;
+
         if(unlikely(w->dead)) {
             debug(D_WEB_CLIENT, "%llu: client is dead.", w->id);
             break;
@@ -2719,6 +1674,8 @@ void *web_client_main(void *ptr)
         timeout = web_client_timeout * 1000;
         retval = poll(fds, fdmax, timeout);
 
+        if(unlikely(netdata_exit)) break;
+
         if(unlikely(retval == -1)) {
             if(errno == EAGAIN || errno == EINTR) {
                 debug(D_WEB_CLIENT, "%llu: EAGAIN received.", w->id);
@@ -2733,6 +1690,8 @@ void *web_client_main(void *ptr)
             break;
         }
 
+        if(unlikely(netdata_exit)) break;
+
         int used = 0;
         if(w->wait_send && ofd->revents & POLLOUT) {
             used++;
@@ -2742,6 +1701,8 @@ void *web_client_main(void *ptr)
             }
         }
 
+        if(unlikely(netdata_exit)) break;
+
         if(w->wait_receive && (ifd->revents & POLLIN || ifd->revents & POLLPRI)) {
             used++;
             if(web_client_receive(w) < 0) {
@@ -2751,7 +1712,12 @@ void *web_client_main(void *ptr)
 
             if(w->mode == WEB_CLIENT_MODE_NORMAL) {
                 debug(D_WEB_CLIENT, "%llu: Attempting to process received data.", w->id);
-                web_client_process(w);
+                web_client_process_request(w);
+
+                // if the sockets are closed, may have transferred this client
+                // to plugins.d
+                if(unlikely(w->mode == WEB_CLIENT_MODE_STREAM))
+                    break;
             }
         }
 
index 76284e590fe5007ccecbed0faca45296185c74ad..70c5b1ff05f66ec4262f1ead9a229e3ac4d93ea9 100644 (file)
@@ -13,9 +13,12 @@ extern int web_enable_gzip,
 extern int respect_web_browser_do_not_track_policy;
 extern char *web_x_frame_options;
 
-#define WEB_CLIENT_MODE_NORMAL      0
-#define WEB_CLIENT_MODE_FILECOPY    1
-#define WEB_CLIENT_MODE_OPTIONS     2
+typedef enum web_client_mode {
+    WEB_CLIENT_MODE_NORMAL      = 0,
+    WEB_CLIENT_MODE_FILECOPY    = 1,
+    WEB_CLIENT_MODE_OPTIONS     = 2,
+    WEB_CLIENT_MODE_STREAM      = 3
+} WEB_CLIENT_MODE;
 
 #define URL_MAX 8192
 #define ZLIB_CHUNK  16384
@@ -55,14 +58,14 @@ struct web_client {
 
     uint8_t keepalive:1;                // if set to 1, the web client will be re-used
 
-    uint8_t mode:3;                     // the operational mode of the client
-
     uint8_t wait_receive:1;             // 1 = we are waiting more input data
     uint8_t wait_send:1;                // 1 = we have data to send to the client
 
     uint8_t donottrack:1;               // 1 = we should not set cookies on this client
     uint8_t tracking_required:1;        // 1 = if the request requires cookies
 
+    WEB_CLIENT_MODE mode;               // the operational mode of the client
+
     int tcp_cork;                       // 1 = we have a cork on the socket
 
     int ifd;
@@ -103,7 +106,7 @@ extern struct web_client *web_client_create(int listener);
 extern struct web_client *web_client_free(struct web_client *w);
 extern ssize_t web_client_send(struct web_client *w);
 extern ssize_t web_client_receive(struct web_client *w);
-extern void web_client_process(struct web_client *w);
+extern void web_client_process_request(struct web_client *w);
 extern void web_client_reset(struct web_client *w);
 
 extern void *web_client_main(void *ptr);
@@ -112,4 +115,7 @@ extern int web_client_api_request_v1_data_group(char *name, int def);
 extern const char *group_method2string(int group);
 
 extern void buffer_data_options2string(BUFFER *wb, uint32_t options);
+
+extern int mysendfile(struct web_client *w, char *filename);
+
 #endif
index 8e942a59d549e8dc380447de1f2f5f1753fa1305..593a82a57f4abfd5a9508130a84cd341d73cee63 100644 (file)
@@ -5,7 +5,8 @@ size_t listen_fds_count = 0;
 int listen_fds[MAX_LISTEN_FDS] = { [0 ... 99] = -1 };
 char *listen_fds_names[MAX_LISTEN_FDS] = { [0 ... 99] = NULL };
 int listen_port = LISTEN_PORT;
-int web_server_mode = WEB_SERVER_MODE_MULTI_THREADED;
+
+WEB_SERVER_MODE web_server_mode = WEB_SERVER_MODE_MULTI_THREADED;
 
 static int shown_server_socket_error = 0;
 
@@ -83,6 +84,29 @@ int accept4(int sock, struct sockaddr *addr, socklen_t *addrlen, int flags) {
 }
 #endif
 
+WEB_SERVER_MODE web_server_mode_id(const char *mode) {
+    if(!strcmp(mode, "none"))
+        return WEB_SERVER_MODE_NONE;
+    else if(!strcmp(mode, "single") || !strcmp(mode, "single-threaded"))
+        return WEB_SERVER_MODE_SINGLE_THREADED;
+    else // if(!strcmp(mode, "multi") || !strcmp(mode, "multi-threaded"))
+        return WEB_SERVER_MODE_MULTI_THREADED;
+}
+
+const char *web_server_mode_name(WEB_SERVER_MODE id) {
+    switch(id) {
+        case WEB_SERVER_MODE_NONE:
+            return "none";
+
+        case WEB_SERVER_MODE_SINGLE_THREADED:
+            return "single-threaded";
+
+        default:
+        case WEB_SERVER_MODE_MULTI_THREADED:
+            return "multi-threaded";
+    }
+}
+
 int create_listen_socket4(const char *ip, int port, int listen_backlog) {
     int sock;
     int sockopt = 1;
@@ -316,22 +340,16 @@ static inline int bind_to_one(const char *definition, int default_port, int list
 int create_listen_sockets(void) {
     shown_server_socket_error = 0;
 
-    listen_backlog = (int) config_get_number("global", "http port listen backlog", LISTEN_BACKLOG);
-
-    if(config_exists("global", "bind socket to IP") && !config_exists("global", "bind to"))
-        config_rename("global", "bind socket to IP", "bind to");
-
-    if(config_exists("global", "port") && !config_exists("global", "default port"))
-        config_rename("global", "port", "default port");
+    listen_backlog = (int) config_get_number(CONFIG_SECTION_WEB, "listen backlog", LISTEN_BACKLOG);
 
-    listen_port = (int) config_get_number("global", "default port", LISTEN_PORT);
+    listen_port = (int) config_get_number(CONFIG_SECTION_WEB, "default port", LISTEN_PORT);
     if(listen_port < 1 || listen_port > 65535) {
         error("Invalid listen port %d given. Defaulting to %d.", listen_port, LISTEN_PORT);
-        listen_port = (int) config_set_number("global", "default port", LISTEN_PORT);
+        listen_port = (int) config_set_number(CONFIG_SECTION_WEB, "default port", LISTEN_PORT);
     }
     debug(D_OPTIONS, "Default listen port set to %d.", listen_port);
 
-    char *s = config_get("global", "bind to", "*");
+    char *s = config_get(CONFIG_SECTION_WEB, "bind to", "*");
     while(*s) {
         char *e = s;
 
@@ -614,7 +632,7 @@ void *socket_listen_main_single_threaded(void *ptr) {
 
                     if (w->mode != WEB_CLIENT_MODE_FILECOPY) {
                         debug(D_WEB_CLIENT, "%llu: Processing received data.", w->id);
-                        web_client_process(w);
+                        web_client_process_request(w);
                     }
                 }
 
index 93adc5b28a847f0f5c961f3a56cea86aaae17b70..41dcfcf0957d15d7f5ed8a638ac530d94dc51ded 100644 (file)
 #define MAX_LISTEN_FDS 100
 #endif
 
-#define WEB_SERVER_MODE_MULTI_THREADED 0
-#define WEB_SERVER_MODE_SINGLE_THREADED 1
-extern int web_server_mode;
+typedef enum web_server_mode {
+    WEB_SERVER_MODE_SINGLE_THREADED,
+    WEB_SERVER_MODE_MULTI_THREADED,
+    WEB_SERVER_MODE_NONE
+} WEB_SERVER_MODE;
+
+extern WEB_SERVER_MODE web_server_mode;
+
+extern WEB_SERVER_MODE web_server_mode_id(const char *mode);
+extern const char *web_server_mode_name(WEB_SERVER_MODE id);
+
 
 extern void *socket_listen_main_multi_threaded(void *ptr);
 extern void *socket_listen_main_single_threaded(void *ptr);
index 2fa38802e485cce760928022a92294992adcdcda..6f6a4eafbf0b104d517f313f46200c25d529fb94 100644 (file)
@@ -671,7 +671,7 @@ p {
                     can be set on any metric monitored by netdata.
                     Alarm <a href="https://github.com/firehol/netdata/wiki/health-monitoring#alarm-actions" target="_blank" data-ga-category="Outbound links" data-ga-action="Nav click" data-ga-label=AlarmNotifications>notifications</a>
                     are role-based and support dynamic thresholds, hysteresis and can be dispatched via multiple methods
-                    (such as email, slack.com, pushover.net, pushbullet.com telegram.org, twilio.com).
+                    (such as email, slack.com, pushover.net, pushbullet.com, telegram.org, twilio.com).
             </div>
             <div class=grid-cell><h3><span class=star>&#x2605;</span> In real-time</h3>
                 <p>netdata collects thousands of metrics per server <strong>per second</strong>,
index 5589255810e003dd847f08c0b2e6a71f9c34e4a7..e0e7158a6c1febdace43f4c47747878283cd743d 100644 (file)
         var netdataRegistryCallback = function(machines_array) {
             var el = '';
             var a1 = '';
-            var found = 0;
+            var found = 0, hosted = 0;
+            var len, i, url, hostname, icon;
+
+            if(options.hosts.length > 1) {
+                el += '<li><a href="#" onClick="return false;" style="color: #666;" target="_blank">databases available on this host</a></li>';
+                a1 += '<li><a href="#" onClick="return false;"><i class="fa fa-info-circle" aria-hidden="true" style="color: #666;"></i></a></li>';
+
+                var base = document.location.origin.toString() + document.location.pathname.toString();
+                if(base.endsWith("/host/" + options.hostname + "/"))
+                    base = base.substring(0, base.length - ("/host/" + options.hostname + "/").toString().length);
+
+                if(base.endsWith("/"))
+                    base = base.substring(0, base.length - 1);
+
+                i = 0;
+                len = options.hosts.length;
+                while(len--) {
+                    hostname = options.hosts[i].hostname;
+                    if(i == 0) {
+                        url = base + "/";
+                        icon = "home";
+                    }
+                    else {
+                        url = base + "/host/" + hostname + "/";
+                        icon = "window-restore";
+                    }
+
+                    el += '<li id="registry_server_hosted_' + len.toString() + '"><a class="registry_link" href="' + url + '" onClick="return gotoHostedModalHandler(\'' + url + '\');">' + hostname + '</a></li>';
+                    a1 += '<li id="registry_action_hosted_' + len.toString() + '"><a class="registry_link" href="' + url + '" onClick="return gotoHostedModalHandler(\'' + url + '\');"><i class="fa fa-' + icon + '" aria-hidden="true" style="color: #999;"></i></a></li>';
+                    hosted++;
+                    i++;
+                }
+
+                el += '<li role="separator" class="divider"></li>';
+                a1 += '<li role="separator" class="divider"></li>';
+            }
 
             if(machines_array === null) {
                 var ret = loadLocalStorage("registryCallback");
                     return 0;
                 });
 
-                var len = machines.length;
+                len = machines.length;
                 while(len--) {
                     var u = machines[len];
                     found++;
             location.reload();
         }
 
+        function gotoHostedModalHandler(url) {
+            document.location = url + urlOptions.genHash();
+            return false;
+        }
+
         var gotoServerValidateRemaining = 0;
         var gotoServerMiddleClick = false;
         var gotoServerStop = false;
             categories_idx: {},
             families: [],
             families_idx: {},
+            hosts: [],
 
             chartsPerRow: 0,
             // chartsMinWidth: 1450,
 
             sidebar += '<li class="" style="padding-top:15px;"><a href="https://github.com/firehol/netdata/wiki/Add-more-charts-to-netdata" target="_blank"><i class="fa fa-plus" aria-hidden="true"></i> add more charts</a></li>';
             sidebar += '<li class=""><a href="https://github.com/firehol/netdata/wiki/Add-more-alarms-to-netdata" target="_blank"><i class="fa fa-plus" aria-hidden="true"></i> add more alarms</a></li>';
-            sidebar += '<li class="" style="margin:20px;color:#666;"><small>netdata on <b>' + data.hostname.toString() + '</b>, collects every ' + ((data.update_every == 1)?'second':data.update_every.toString() + ' seconds') + ' <b>' + data.dimensions_count.toLocaleString() + '</b> metrics, presented as <b>' + data.charts_count.toLocaleString() + '</b> charts and monitored by <b>' + data.alarms_count.toLocaleString() + '</b> alarms, using ' + Math.round(data.rrd_memory_bytes / 1024 / 1024).toLocaleString() + ' MB of memory for ' + Math.round(data.history / (3600/data.update_every)).toLocaleString() + ' ' + ((data.history == (3600/data.update_every))?'hour':'hours').toString() + ' of real-time history.<br/>&nbsp;<br/><b>netdata</b><br/>v' +  data.version.toString() +'</small></li>';
+            sidebar += '<li class="" style="margin:20px;color:#666;"><small>netdata on <b>' + data.hostname.toString() + '</b>, collects every ' + ((data.update_every == 1)?'second':data.update_every.toString() + ' seconds') + ' <b>' + data.dimensions_count.toLocaleString() + '</b> metrics, presented as <b>' + data.charts_count.toLocaleString() + '</b> charts and monitored by <b>' + data.alarms_count.toLocaleString() + '</b> alarms, using ' + Math.round(data.rrd_memory_bytes / 1024 / 1024).toLocaleString() + ' MB of memory for ' + seconds4human(data.update_every * data.history) + ' of real-time history.<br/>&nbsp;<br/><b>netdata</b><br/>v' +  data.version.toString() +'</small></li>';
             sidebar += '</ul>';
             div.innerHTML = html;
             document.getElementById('sidebar').innerHTML = sidebar;
                     return t.toLocaleDateString() + space + t.toLocaleTimeString();
                 }
 
-                function seconds4human(seconds, options) {
-                    var default_options = {
-                        now: 'now',
-                        space: '&nbsp;',
-                        negative_suffix: 'ago',
-                        hour: 'hour',
-                        hours: 'hours',
-                        minute: 'minute',
-                        minutes: 'minutes',
-                        second: 'second',
-                        seconds: 'seconds',
-                        and: 'and'
-                    };
-
-                    if(typeof options !== 'object')
-                        options = default_options;
-                    else {
-                        var x;
-                        for(x in default_options) {
-                            if(typeof options[x] !== 'string')
-                                options[x] = default_options[x];
-                        }
-                    }
-
-                    if(typeof seconds === 'string')
-                        seconds = parseInt(seconds);
-
-                    if(seconds === 0)
-                        return options.now;
-
-                    var suffix = '';
-                    if(seconds < 0) {
-                        seconds = -seconds;
-                        if(options.negative_suffix !== '') suffix = options.space + options.negative_suffix;
-                    }
-
-                    var hours = Math.floor(seconds / 3600);
-                    seconds -= (hours * 3600);
-
-                    var minutes = Math.floor(seconds / 60);
-                    seconds -= (minutes * 60);
-
-                    var txt = '';
-
-                    if(hours > 1) txt += hours.toString() + options.space + options.hours;
-                    else if(hours === 1) txt += hours.toString() + options.space + options.hour;
-
-                    if(hours > 0 && minutes > 0 && seconds == 0)
-                        txt += options.space + options.and + options.space;
-                    else if(hours > 0 && minutes > 0 && seconds > 0)
-                        txt += ',' + options.space;
-
-                    if(minutes > 1) txt += minutes.toString() + options.space + options.minutes;
-                    else if(minutes === 1) txt += minutes.toString() + options.space + options.minute;
-
-                    if((minutes > 0 || minutes > 0) && seconds > 0)
-                        txt += options.space + options.and + options.space;
-
-                    if(seconds > 1) txt += Math.floor(seconds).toString() + options.space + options.seconds;
-                    else if(seconds === 1) txt += Math.floor(seconds).toString() + options.space + options.second;
-
-                    return txt + suffix;
-                }
-
                 function alarm_lookup_explain(alarm, chart) {
                     var dimensions = ' of all values ';
 
             });
         }
 
+        function seconds4human(seconds, options) {
+            var default_options = {
+                now: 'now',
+                space: '&nbsp;',
+                negative_suffix: 'ago',
+                hour: 'hour',
+                hours: 'hours',
+                minute: 'minute',
+                minutes: 'minutes',
+                second: 'second',
+                seconds: 'seconds',
+                and: 'and'
+            };
+
+            if(typeof options !== 'object')
+                options = default_options;
+            else {
+                var x;
+                for(x in default_options) {
+                    if(typeof options[x] !== 'string')
+                        options[x] = default_options[x];
+                }
+            }
+
+            if(typeof seconds === 'string')
+                seconds = parseInt(seconds);
+
+            if(seconds === 0)
+                return options.now;
+
+            var suffix = '';
+            if(seconds < 0) {
+                seconds = -seconds;
+                if(options.negative_suffix !== '') suffix = options.space + options.negative_suffix;
+            }
+
+            var hours = Math.floor(seconds / 3600);
+            seconds -= (hours * 3600);
+
+            var minutes = Math.floor(seconds / 60);
+            seconds -= (minutes * 60);
+
+            var txt = '';
+
+            if(hours > 1) txt += hours.toString() + options.space + options.hours;
+            else if(hours === 1) txt += hours.toString() + options.space + options.hour;
+
+            if(hours > 0 && minutes > 0 && seconds == 0)
+                txt += options.space + options.and + options.space;
+            else if(hours > 0 && minutes > 0 && seconds > 0)
+                txt += ',' + options.space;
+
+            if(minutes > 1) txt += minutes.toString() + options.space + options.minutes;
+            else if(minutes === 1) txt += minutes.toString() + options.space + options.minute;
+
+            if((minutes > 0 || minutes > 0) && seconds > 0)
+                txt += options.space + options.and + options.space;
+
+            if(seconds > 1) txt += Math.floor(seconds).toString() + options.space + options.seconds;
+            else if(seconds === 1) txt += Math.floor(seconds).toString() + options.space + options.second;
+
+            return txt + suffix;
+        }
+
         function alarmsCallback(data) {
             var count = 0;
             for(x in data.alarms) {
                         options.version = data.version;
                         netdataDashboard.os = data.os;
 
+                        if(typeof data.hosts != 'undefined')
+                            options.hosts = data.hosts;
+
                         // update the dashboard hostname
                         document.getElementById('hostname').innerHTML = options.hostname;
                         document.getElementById('hostname').href = NETDATA.serverDefault;