]> arthur.barton.de Git - netdata.git/commitdiff
Merge pull request #879 from ktsaou/master
authorCosta Tsaousis <costa@tsaousis.gr>
Sat, 3 Sep 2016 12:27:41 +0000 (15:27 +0300)
committerGitHub <noreply@github.com>
Sat, 3 Sep 2016 12:27:41 +0000 (15:27 +0300)
alarms now support roles with different recipients - aesthetic changes on the dashboard

25 files changed:
conf.d/health.d/apache.conf
conf.d/health.d/cpu.conf
conf.d/health.d/disks.conf
conf.d/health.d/entropy.conf
conf.d/health.d/memcached.conf
conf.d/health.d/named.conf
conf.d/health.d/net.conf
conf.d/health.d/nginx.conf
conf.d/health.d/qos.conf
conf.d/health.d/ram.conf
conf.d/health.d/redis.conf
conf.d/health.d/squid.conf
conf.d/health.d/swap.conf
conf.d/health_email_recipients.conf [new file with mode: 0755]
configs.signatures
node.d/sma_webbox.node.js
plugins.d/alarm-email.sh
src/health.c
src/health.h
src/main.c
src/registry.c
src/rrd.c
src/rrd.h
src/rrd2json.c
web/index.html

index 1fddbc99f2fea924fcb8f084f5cf96613938545d..72d6be4c27bd4eab548154fb601146f611ca4d9a 100644 (file)
@@ -6,8 +6,8 @@ template: apache_last_collected_secs
     calc: $now - $last_collected_t
    every: 10s
     warn: $this > ( 5 * $update_every)
-    crit: $this > (10 * $update_every)
+    crit: $this > (60 * $update_every)
    units: seconds ago
     info: number of seconds since the last successful data collection
-
+      to: webmaster
 
index 9332e508a3d7009cfab585dcb64a0859a0075eca..8caee3259cc61e4f38f97ad55362e9860da44894 100644 (file)
@@ -6,6 +6,7 @@ template: 5min_cpu_pcent
     warn: $this > 90
    units: %
     info: average cpu utilization for the last 5 minutes
+      to: sysadmin
 
 template: 5min_iowait_cpu_pcent
       on: system.cpu
@@ -14,6 +15,7 @@ template: 5min_iowait_cpu_pcent
     warn: $this > 10
    units: %
     info: average wait I/O for the last 5 minutes
+      to: sysadmin
 
 template: 20min_steal_cpu_pcent
       on: system.cpu
@@ -22,3 +24,4 @@ template: 20min_steal_cpu_pcent
     warn: $this > 10
    units: %
     info: average stolen CPU time for the last 20 minutes
+      to: sysadmin
index 2398e2c6429755636dbe3f3125a58100572e9470..7e98511feb4a7a5e14f9e3da62a15376873b0ee3 100644 (file)
@@ -13,6 +13,7 @@ template: disk_full_percent
     crit: $this > 95
    units: %
     info: current disk space usage
+      to: sysadmin
 
 
 # -----------------------------------------------------------------------------
@@ -47,6 +48,7 @@ template: disk_full_after_hours
     crit: $this > 0 and $this < 24
    units: hours
     info: estimated time the disk will run out of space, if the system continues to add data with the rate of the last 30 minutes
+      to: sysadmin
 
 
 # -----------------------------------------------------------------------------
@@ -66,6 +68,7 @@ template: 10min_disk_utilization
     crit: $this > $red
    units: %
     info: the percentage of time the disk was busy, during the last 10 minutes
+      to: sysadmin
 
 
 # raise an alarm if the disk backlog
@@ -83,3 +86,4 @@ template: 10min_disk_backlog
     crit: $this > $red
    units: ms
     info: average of the kernel estimated disk backlog, for the last 10 minutes
+      to: sysadmin
index 6f8b6e85182fd4a162f6d19659d75e12d7056d18..fcbfb4cb54a93325dea342e5c0e760556e94cb31 100644 (file)
@@ -11,3 +11,4 @@
     crit: $this < 100
    units: entries
     info: minimum entries in the random numbers pool (entropy), for the last 30 minutes
+      to: sysadmin
index 05ff14711832ebd469ece4052a9259d712c53f57..6e1acac2f5120e3b99e54cfeefb571589556f1d5 100644 (file)
@@ -6,9 +6,10 @@ template: memcached_last_collected_secs
     calc: $now - $last_collected_t
    every: 10s
     warn: $this > ( 5 * $update_every)
-    crit: $this > (10 * $update_every)
+    crit: $this > (60 * $update_every)
    units: seconds ago
     info: number of seconds since the last successful data collection
+      to: dba
 
 
 # detect if memcached cache is full
@@ -21,6 +22,7 @@ template: cache_full_pcent
     crit: $this > 90
    units: %
     info: current cache memory usage
+      to: dba
 
 
 # find the rate memcached cache is filling
@@ -44,3 +46,4 @@ template: cache_full_after_hours
     crit: $this > 0 and $this < 24
    units: hours
     info: estimated time the cache will run out of space, if the system continues to add data with the rate of the last 30 minutes
+      to: dba
index e46d1d33005e8da54cf73ad3adf196db82732613..997c6b94fa3bf0951fd0109ef5de14cf9c1c366e 100644 (file)
@@ -6,7 +6,8 @@ template: named_last_collected_secs
     calc: $now - $last_collected_t
    every: 10s
     warn: $this > ( 5 * $update_every)
-    crit: $this > (10 * $update_every)
+    crit: $this > (60 * $update_every)
    units: seconds ago
     info: number of seconds since the last successful data collection
+      to: domainadmin
 
index f65bc4fcb0783ac8a9d4a99c75e69f1b5c04a03a..209dbda6abca238ce6a487e853b8eb4828263915 100644 (file)
@@ -10,6 +10,7 @@ template: 30min_packet_drops
     crit: $this > 0
    units: packets
     info: dropped packets in the last 30 minutes
+      to: sysadmin
 
 
 # check if an interface is having FIFO
@@ -24,4 +25,5 @@ template: 30min_fifo_errors
     crit: $this > 0
    units: errors
     info: network interface fifo errors in the last 30 minutes
+      to: sysadmin
 
index da13008e396d92010a1caf17771700a002a54373..880777d744c8130d9475ef129a5b16da0a152875 100644 (file)
@@ -6,7 +6,8 @@ template: nginx_last_collected_secs
     calc: $now - $last_collected_t
    every: 10s
     warn: $this > ( 5 * $update_every)
-    crit: $this > (10 * $update_every)
+    crit: $this > (60 * $update_every)
    units: seconds ago
     info: number of seconds since the last successful data collection
+      to: webmaster
 
index ac3bf8ff4153fed9f91c3b73b1310b7b3287aa0f..bd438aa0b9673b5a274aa1bbbff2a001bbe48ace 100644 (file)
@@ -10,3 +10,4 @@
 #    warn: $this > 0
 #   units: packets
 #    info: dropped packets in the last 30 minutes
+#      to: sysadmin
index 1d368112838464466a7231f54c412649282a6092..8585dfa69b6ff84f2d8632ff5e73f359bf0df366 100644 (file)
@@ -7,3 +7,4 @@
     crit: $this > 90
    units: %
     info: system RAM usage
+      to: sysadmin
index 3750176c5f5864d055d25a0b34783090d0a57ce1..cdeae4a5bd25b4b98b99f90baa33f19499cd9676 100644 (file)
@@ -6,7 +6,8 @@ template: redis_last_collected_secs
     calc: $now - $last_collected_t
    every: 10s
     warn: $this > ( 5 * $update_every)
-    crit: $this > (10 * $update_every)
+    crit: $this > (60 * $update_every)
    units: seconds ago
     info: number of seconds since the last successful data collection
+      to: dba
 
index cc5ce1c3a45fbefe0786ca5035e14fc1a25252d6..8fecce2312324ed301078a0effc06277e98c39c4 100644 (file)
@@ -6,7 +6,8 @@ template: squid_last_collected_secs
     calc: $now - $last_collected_t
    every: 10s
     warn: $this > ( 5 * $update_every)
-    crit: $this > (10 * $update_every)
+    crit: $this > (60 * $update_every)
    units: seconds ago
     info: number of seconds since the last successful data collection
+      to: proxyadmin
 
index 1420565bddfc3010b99477e4cf8697053ea4c71c..f762b03079371a135dfc4db953d4a10cfc8f8119 100644 (file)
@@ -9,6 +9,7 @@
     crit: $this > 20
    units: % of RAM
     info: the amount of memory swapped in the last 30 minutes, as a percentage of the system RAM
+      to: sysadmin
 
    alarm: pcent_of_ram_in_swap
       on: system.swap
@@ -18,3 +19,4 @@
     crit: $this > 50
    units: % of RAM
     info: the swap memory used, as a percentage of the system RAM
+      to: sysadmin
diff --git a/conf.d/health_email_recipients.conf b/conf.d/health_email_recipients.conf
new file mode 100755 (executable)
index 0000000..e485591
--- /dev/null
@@ -0,0 +1,53 @@
+# Configuration for alarms recipients
+
+# netdata alarms have been categorized to allow different roles to receive
+# alarms related to their work.
+
+# this file defines the email addresses for each role. if a role is not
+# defined, the email will be sent to root.
+
+# you can set multiple addresses for each role, like this:
+#
+# recipients[sysadmin]="admin1@example.com, admin2@example.com"
+#
+# it is important to add the comma between email addresses.
+
+# This configuration file is a BASH script itself. The 'recipients' variable is
+# an associative array. So you can use other variables too, like this one:
+
+default_recipient_for_all_roles="root"
+
+
+# -----------------------------------------------------------------------------
+# generic system alarms
+# CPU, disks, entropy, etc
+
+recipients[sysadmin]="${default_recipient_for_all_roles}"
+
+
+# -----------------------------------------------------------------------------
+# DNS related alarms
+
+recipients[domainadmin]="${default_recipient_for_all_roles}"
+
+
+# -----------------------------------------------------------------------------
+# database servers alarms
+# mysql, redis, memcached, etc
+
+recipients[dba]="${default_recipient_for_all_roles}"
+
+
+# -----------------------------------------------------------------------------
+# web servers alarms
+# apache, nginx, etc
+
+recipients[webmaster]="${default_recipient_for_all_roles}"
+
+
+# -----------------------------------------------------------------------------
+# proxy servers alarms
+# apache, nginx, etc
+
+recipients[proxyadmin]="${default_recipient_for_all_roles}"
+
index 425d3169c560e6a791172a8124bfbd897c29299b..ee07246bf5183e53b38950d21d1cc8c39573f665 100644 (file)
@@ -29,9 +29,12 @@ declare -A configs_signatures=(
   ['7cf6402b51e5070f2be3ad6fe059ff89']='charts.d.conf'
   ['a02d14124b19c635c1426cee2e98bac5']='charts.d.conf'
   ['ca026d7c779f0a7cb7787713c5be5c47']='charts.d.conf'
+  ['074df527cc70b5f38c0714f08f20e57c']='health.d/apache.conf'
   ['3848172053221b95279ba9bf789cd4e0']='health.d/apache.conf'
   ['842b1ad5b89bfa5f421d9c5b72e001a4']='health.d/apache.conf'
+  ['a6d5ce2572bf7a1dce9e545fcd29273e']='health.d/apache.conf'
   ['084ee72d64760f2641b0720e79c922f3']='health.d/cpu.conf'
+  ['254de8ec49602bea2da3631676d7cfec']='health.d/cpu.conf'
   ['623771eecb3c277fc728b5304793f93b']='health.d/cpu.conf'
   ['7596ae54d46ce199ac599429ef753caf']='health.d/cpu.conf'
   ['fdd11640ba626cc2064c2fe3ea3eee4c']='health.d/cpu.conf'
@@ -47,39 +50,53 @@ declare -A configs_signatures=(
   ['a8167dafeac0b66696a1d9b08e815cda']='health.d/disks.conf'
   ['afdae4646c755ff2d117527fbf761c8e']='health.d/disks.conf'
   ['cb60badf376d246ad8ec9d3f524db430']='health.d/disks.conf'
+  ['dd8254ef74509a3e38cb2838e30f7e63']='health.d/disks.conf'
   ['e8ec8046c7007af6ca3e8c51e62c99f8']='health.d/disks.conf'
   ['2d1d7498c72f4245cf32902c2b7e71e0']='health.d/entropy.conf'
   ['450667c552ab7a7d8d4a2c214fdacca5']='health.d/entropy.conf'
+  ['5ff1bcaa58695754e2f6980bfe19f579']='health.d/entropy.conf'
   ['a8feb36776005bf419c90278787a1be8']='health.d/entropy.conf'
   ['d8dc489e32f7114c6298fce94e86a8ef']='health.d/entropy.conf'
   ['e449e5582279742496550df14b6fca95']='health.d/entropy.conf'
   ['45a77ac36ba9f1898144b902de17204b']='health.d/memcached.conf'
+  ['621f10b257a11add5ff5aff41e9662e3']='health.d/memcached.conf'
+  ['7e5fc1644aa7a54f9dbb1bd102521b09']='health.d/memcached.conf'
   ['b81b8f331161b0d48e03f6fbf6b6d062']='health.d/memcached.conf'
   ['c1a7e634b5b8aad523a0d115a93379cd']='health.d/memcached.conf'
   ['f8c30f22df92765e2c0fab3c8174e2fc']='health.d/memcached.conf'
   ['373c1276dc9e65884ff2b26e1f08afe7']='health.d/named.conf'
+  ['6b39de5d85db45115db236347a6896d4']='health.d/named.conf'
   ['846ce94bfeeb90c0dc6a89e8d25f1a68']='health.d/named.conf'
   ['ddda2bb1c88be03b637d3285406f7910']='health.d/named.conf'
   ['2827de41cf34a91b7a8e4d8724f59668']='health.d/net.conf'
   ['2ad55a5d1e885cf142849a78d4b00401']='health.d/net.conf'
   ['43ebb7f224c3b232d8ad044d7e9508b6']='health.d/net.conf'
+  ['cb178b15427274d7def5b14bc4c09441']='health.d/net.conf'
   ['d11711b3647bc2bdd0292dd7deebbeb1']='health.d/net.conf'
   ['de02f899a61f21b86adb646940f0bcae']='health.d/net.conf'
+  ['32fde0057c790964f2c743cb3c9aad29']='health.d/nginx.conf'
   ['64c48f9726ab987baec9c617a9fef7a6']='health.d/nginx.conf'
   ['7a985528cc9176564640001aa73e3492']='health.d/nginx.conf'
+  ['eb5168f0b516bc982aac45e59da6e52e']='health.d/nginx.conf'
   ['36fdd55665cf10b0db164c2a0cca5e57']='health.d/qos.conf'
+  ['55608bdd908a3806df1468f6ee318b2b']='health.d/qos.conf'
   ['80f109ff293ac94222bf3959432751bd']='health.d/qos.conf'
   ['20be73f473e59bc7de1fe61d53466aba']='health.d/ram.conf'
+  ['a09714b5942cf25a89ec3da1dbc18063']='health.d/ram.conf'
   ['b7d769ce86a7aebba01315da5c0799e6']='health.d/ram.conf'
   ['325617412a628e3bc776e3fbb777a2a6']='health.d/redis.conf'
+  ['3634d5eddc46fb0d50cf47f370670c2c']='health.d/redis.conf'
   ['4f6a5b47a13f5912cc89e9286701dd08']='health.d/redis.conf'
   ['23ae815aefa221b1929f96752a1f7556']='health.d/squid.conf'
+  ['3cc6255457d4cba881ae0554ae5d9190']='health.d/squid.conf'
   ['a4a8660728c6afcb528cc6b378897d6b']='health.d/squid.conf'
   ['ef9916ea144878a9f37cbb6b1b29da10']='health.d/squid.conf'
+  ['8214bb8f4b005aa4691fcd38f7331e8f']='health.d/swap.conf'
   ['a44899a5795bed2863c1d11aa3e85586']='health.d/swap.conf'
   ['a7320c6f26191b9599ec3bc4be007a93']='health.d/swap.conf'
   ['c9b792755de59d842ba95f8c315d94c8']='health.d/swap.conf'
   ['da29d2ab1ab7b8fda189960c840e5144']='health.d/swap.conf'
+  ['707a63f53f4b32e01d134ae90ba94aad']='health_email_recipients.conf'
   ['13141998a5d71308d9c119834c27bfd3']='python.d.conf'
   ['38d1bf04fe9901481dd6febcc0404a86']='python.d.conf'
   ['4b775fb31342f1478b3773d041a72911']='python.d.conf'
index a6ce12052a61e34d640458ca09fd3b4c8a32a712..3dd17a5c123172ff1732b636c431bf34b79df258 100644 (file)
@@ -74,7 +74,7 @@ var webbox = {
 
             // Grid Current Power Chart
             if(d['GriPwr'].value !== null) {
-                var id = 'sma_webbox_' + service.name + '.current';
+                var id = 'smawebbox_' + service.name + '.current';
                 var chart = webbox.charts[id];
 
                 if(typeof chart === 'undefined') {
@@ -84,7 +84,7 @@ var webbox = {
                         title: service.name + ' Current Grid Power',    // the title of the chart
                         units: d['GriPwr'].unit,                        // the units of the chart dimensions
                         family: 'now',                                  // the family of the chart
-                        context: 'sma_webbox.grid.power',               // the context of the chart
+                        context: 'smawebbox.grid_power',                // the context of the chart
                         type: netdata.chartTypes.area,                  // the type of the chart
                         priority: webbox.base_priority + 1,             // the priority relative to others in the same family
                         update_every: service.update_every,             // the expected update frequency of the chart
@@ -110,7 +110,7 @@ var webbox = {
             }
 
             if(d['GriEgyTdy'].value !== null) {
-                var id = 'sma_webbox_' + service.name + '.today';
+                var id = 'smawebbox_' + service.name + '.today';
                 var chart = webbox.charts[id];
 
                 if(typeof chart === 'undefined') {
@@ -120,7 +120,7 @@ var webbox = {
                         title: service.name + ' Today Grid Power',      // the title of the chart
                         units: d['GriEgyTdy'].unit,                     // the units of the chart dimensions
                         family: 'today',                                // the family of the chart
-                        context: 'sma_webbox.grid.power.today',         // the context of the chart
+                        context: 'smawebbox.grid_power_today',          // the context of the chart
                         type: netdata.chartTypes.area,                  // the type of the chart
                         priority: webbox.base_priority + 2,             // the priority relative to others in the same family
                         update_every: service.update_every,             // the expected update frequency of the chart
@@ -146,7 +146,7 @@ var webbox = {
             }
 
             if(d['GriEgyTot'].value !== null) {
-                var id = 'sma_webbox_' + service.name + '.total';
+                var id = 'smawebbox_' + service.name + '.total';
                 var chart = webbox.charts[id];
 
                 if(typeof chart === 'undefined') {
@@ -156,7 +156,7 @@ var webbox = {
                         title: service.name + ' Total Grid Power',      // the title of the chart
                         units: d['GriEgyTot'].unit,                     // the units of the chart dimensions
                         family: 'total',                                // the family of the chart
-                        context: 'sma_webbox.grid.power.total',         // the context of the chart
+                        context: 'smawebbox.grid_power_total',          // the context of the chart
                         type: netdata.chartTypes.area,                  // the type of the chart
                         priority: webbox.base_priority + 3,             // the priority relative to others in the same family
                         update_every: service.update_every,             // the expected update frequency of the chart
index 78c79ccdb72359088918f56ba0fa10018bea93ea..3f1176b156f5a2475044f198f05c022f50ee920a 100755 (executable)
@@ -8,6 +8,13 @@ then
     echo >&2 "I cannot send emails - there is no sendmail command available."
 fi
 
+default_recipient_for_all_roles="root"
+declare -A recipients=()
+if [ -f "${NETDATA_CONFIG_DIR}/health_email_recipients.conf" ]
+    then
+    source "${NETDATA_CONFIG_DIR}/health_email_recipients.conf"
+fi
+
 sendmail_from_pipe() {
     "${sendmail}" -t
 
@@ -21,31 +28,43 @@ sendmail_from_pipe() {
     fi
 }
 
-name="${1}"       # the name of the alarm, as given in netdata health.d entries
-chart="${2}"      # the name of the chart (type.id)
-family="${3}"     # the family of the chart
-status="${4}"     # the current status : UNITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL
-old_status="${5}" # the previous status: UNITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL
-value="${6}"      # the current value
-old_value="${7}"  # the previous value
-src="${8}"        # the line number and file the alarm has been configured
-duration="${9}"   # the duration in seconds the previous state took
-non_clear_duration="${10}" # the total duration in seconds this is non-clear
-units="${11}"     # the units of the value
-info="${12}"      # a short description of the alarm
+recipient="${1}"   # the recepient of the email
+hostname="${2}"    # the hostname this event refers to
+unique_id="${3}"   # the unique id of this event
+alarm_id="${4}"    # the unique id of the alarm that generated this event
+event_id="${5}"    # the incremental id of the event, for this alarm
+when="${6}"        # the timestamp this event occured
+name="${7}"        # the name of the alarm, as given in netdata health.d entries
+chart="${8}"       # the name of the chart (type.id)
+family="${9}"      # the family of the chart
+status="${10}"     # the current status : UNITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL
+old_status="${11}" # the previous status: UNITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL
+value="${12}"      # the current value
+old_value="${13}"  # the previous value
+src="${14}"        # the line number and file the alarm has been configured
+duration="${15}"   # the duration in seconds the previous state took
+non_clear_duration="${16}" # the total duration in seconds this is non-clear
+units="${17}"      # the units of the value
+info="${18}"       # a short description of the alarm
+
+to="${recipients[${recipient}]}"
+[ -z "${to}" ] && to="${default_recipient_for_all_roles}"
+[ -z "${to}" ] && to="root"
 
 [ ! -z "${info}" ] && info=" <small><br/>${info}</small>"
 
 # get the system hostname
-hostname="${NETDATA_HOSTNAME}"
+[ -z "${hostname}" ] && hostname="${NETDATA_HOSTNAME}"
 [ -z "${hostname}" ] && hostname="${NETDATA_REGISTRY_HOSTNAME}"
-[ -z "${hostname}" ] && hostname="$(hostname)"
+[ -z "${hostname}" ] && hostname="$(hostname 2>/dev/null)"
 
 goto_url="${NETDATA_REGISTRY_URL}/goto-host-from-alarm.html?machine_guid=${NETDATA_REGISTRY_UNIQUE_ID}&chart=${chart}&family=${family}"
 
-# get the current date
-date="$(date)"
+date="$(date --date=@${when} 2>/dev/null)"
+[ -z "${date}" ] && date="$(date 2>/dev/null)"
 
+# convert a duration in seconds, to a human readable duration
+# using DAYS, MINUTES, SECONDS
 duration4human() {
     local s="${1}" d=0 h=0 m=0 ds="day" hs="hour" ms="minute" ss="second"
     d=$(( s / 86400 ))
@@ -94,7 +113,7 @@ duration4human() {
 }
 
 severity="${status}"
-raised_for="<br/>(was ${old_status,,} for $(duration4human ${duration}))"
+raised_for="<br/><small>(was ${old_status,,} for $(duration4human ${duration}))</small>"
 status_message="status unknown"
 color="grey"
 alarm="${name} = ${value} ${units}"
@@ -136,7 +155,7 @@ then
     severity="Recovered from ${old_status}"
     if [ $non_clear_duration -gt $duration ]
     then
-        raised_for="<br/>(had issues for $(duration4human ${non_clear_duration}))"
+        raised_for="<br/><small>(had issues for $(duration4human ${non_clear_duration}))</small>"
     fi
 
 elif [ "${old_status}" = "WARNING" -a "${status}" = "CRITICAL" ]
@@ -144,7 +163,7 @@ then
     severity="Escalated to ${status}"
     if [ $non_clear_duration -gt $duration ]
     then
-        raised_for="<br/>(has issues for $(duration4human ${non_clear_duration}))"
+        raised_for="<br/><small>(has issues for $(duration4human ${non_clear_duration}))</small>"
     fi
 
 elif [ "${old_status}" = "CRITICAL" -a "${status}" = "WARNING" ]
@@ -152,7 +171,7 @@ then
     severity="Demoted to ${status}"
     if [ $non_clear_duration -gt $duration ]
     then
-        raised_for="<br/>(has issues for $(duration4human ${non_clear_duration}))"
+        raised_for="<br/><small>(has issues for $(duration4human ${non_clear_duration}))</small>"
     fi
 
 else
@@ -161,7 +180,7 @@ fi
 
 # send the email
 cat <<EOF | sendmail_from_pipe
-To: root
+To: ${to}
 Subject: ${hostname} ${status_message} - ${chart}.${name}
 Content-Type: text/html
 
index 4adac154477238eb9fe1bef5b034a606c9dbb38b..80d506a5c3fdddb749807d8b4002825438873fef 100644 (file)
@@ -3,6 +3,7 @@
 #define RRDVAR_MAX_LENGTH 1024
 
 static const char *health_default_exec = PLUGINS_DIR "/alarm-email.sh";
+static const char *health_default_recipient = "root";
 int health_enabled = 1;
 
 // ----------------------------------------------------------------------------
@@ -618,7 +619,7 @@ static inline RRDCALC *rrdcalc_create(RRDHOST *host, const char *name, const cha
                         const char *units, const char *info,
                         int group_method, int after, int before, int update_every, uint32_t options,
                         calculated_number green, calculated_number red,
-                        const char *exec, const char *source,
+                        const char *exec, const char *recipient, const char *source,
                         const char *calc, const char *warn, const char *crit) {
 
     char fullname[RRDVAR_MAX_LENGTH + 1];
@@ -628,10 +629,10 @@ static inline RRDCALC *rrdcalc_create(RRDHOST *host, const char *name, const cha
         return NULL;
 
     RRDCALC *rc = callocz(1, sizeof(RRDCALC));
-
+    rc->id = host->health_log.next_alarm_id++;
+    rc->next_event_id = 1;
     rc->name = strdupz(name);
     rc->hash = simple_hash(rc->name);
-
     rc->chart = strdupz(chart);
     rc->hash_chart = simple_hash(rc->chart);
 
@@ -649,6 +650,7 @@ static inline RRDCALC *rrdcalc_create(RRDHOST *host, const char *name, const cha
     rc->options = options;
 
     if(exec) rc->exec = strdupz(exec);
+    if(recipient) rc->recipient = strdupz(recipient);
     if(source) rc->source = strdupz(source);
     if(units) rc->units = strdupz(units);
     if(info) rc->info = strdupz(info);
@@ -669,10 +671,11 @@ static inline RRDCALC *rrdcalc_create(RRDHOST *host, const char *name, const cha
             error("Health alarm '%s.%s': failed to re-parse critical expression '%s'", chart, name, crit);
     }
 
-    debug(D_HEALTH, "Health runtime added alarm '%s.%s': exec '%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",
+    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",
           (rc->chart)?rc->chart:"NOCHART",
           rc->name,
           (rc->exec)?rc->exec:"DEFAULT",
+          (rc->recipient)?rc->recipient:"DEFAULT",
           rc->green,
           rc->red,
           rc->group,
@@ -724,6 +727,7 @@ void rrdcalc_free(RRDHOST *host, RRDCALC *rc) {
     freez(rc->family);
     freez(rc->dimensions);
     freez(rc->exec);
+    freez(rc->recipient);
     freez(rc->source);
     freez(rc->units);
     freez(rc->info);
@@ -740,7 +744,8 @@ void rrdcalctemplate_link_matching(RRDSET *st) {
         if(rt->hash_context == st->hash_context && !strcmp(rt->context, st->context)) {
             RRDCALC *rc = rrdcalc_create(st->rrdhost, rt->name, st->id,
                            rt->dimensions, rt->units, rt->info, rt->group, rt->after, rt->before, rt->update_every, rt->options,
-                           rt->green, rt->red, rt->exec, rt->source,
+                           rt->green, rt->red,
+                           rt->exec, rt->recipient, rt->source,
                            (rt->calculation)?rt->calculation->source:NULL,
                            (rt->warning)?rt->warning->source:NULL,
                            (rt->critical)?rt->critical->source:NULL);
@@ -781,6 +786,7 @@ static inline void rrdcalctemplate_free(RRDHOST *host, RRDCALCTEMPLATE *rt) {
 
     freez(rt->name);
     freez(rt->exec);
+    freez(rt->recipient);
     freez(rt->context);
     freez(rt->source);
     freez(rt->units);
@@ -805,6 +811,7 @@ static inline void rrdcalctemplate_free(RRDHOST *host, RRDCALCTEMPLATE *rt) {
 #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"
 
@@ -832,10 +839,11 @@ static inline int rrdcalc_add_alarm_from_config(RRDHOST *host, RRDCALC *rc) {
         return 0;
     }
 
-    debug(D_HEALTH, "Health configuration adding alarm '%s.%s': exec '%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",
+    debug(D_HEALTH, "Health configuration adding 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",
           rc->chart?rc->chart:"NOCHART",
           rc->name,
           (rc->exec)?rc->exec:"DEFAULT",
+          (rc->recipient)?rc->recipient:"DEFAULT",
           rc->green,
           rc->red,
           rc->group,
@@ -878,10 +886,11 @@ static inline int rrdcalctemplate_add_template_from_config(RRDHOST *host, RRDCAL
         }
     }
 
-    debug(D_HEALTH, "Health configuration adding template '%s': context '%s', exec '%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'",
+    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'",
           rt->name,
           (rt->context)?rt->context:"NONE",
           (rt->exec)?rt->exec:"DEFAULT",
+          (rt->recipient)?rt->recipient:"DEFAULT",
           rt->green,
           rt->red,
           rt->group,
@@ -1067,7 +1076,7 @@ static inline void strip_quotes(char *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_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;
+    static uint32_t hash_alarm = 0, hash_template = 0, hash_on = 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;
     char buffer[HEALTH_CONF_MAX_LINE + 1];
 
     if(unlikely(!hash_alarm)) {
@@ -1084,6 +1093,7 @@ int health_readfile(const char *path, const char *filename) {
         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);
     }
 
     snprintfz(buffer, HEALTH_CONF_MAX_LINE, "%s/%s", path, filename);
@@ -1155,6 +1165,8 @@ int health_readfile(const char *path, const char *filename) {
             }
 
             rc = callocz(1, sizeof(RRDCALC));
+            rc->id = localhost.health_log.next_alarm_id++;
+            rc->next_event_id = 1;
             rc->name = strdupz(value);
             rc->hash = simple_hash(rc->name);
             rc->source = health_source_file(line, path, filename);
@@ -1261,6 +1273,16 @@ int health_readfile(const char *path, const char *filename) {
                 }
                 rc->exec = strdupz(value);
             }
+            else if(hash == hash_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) {
+                if(rc->recipient) {
+                    if(strcmp(rc->recipient, value))
+                        info("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+                             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))
@@ -1363,6 +1385,16 @@ int health_readfile(const char *path, const char *filename) {
                 }
                 rt->exec = strdupz(value);
             }
+            else if(hash == hash_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) {
+                if(rt->recipient) {
+                    if(strcmp(rt->recipient, value))
+                        info("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+                             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))
@@ -1493,7 +1525,9 @@ static inline void health_string2json(BUFFER *wb, const char *prefix, const char
 
 static inline void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae) {
     buffer_sprintf(wb, "\n\t{\n"
-                           "\t\t\"id\":%u,\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"
@@ -1502,6 +1536,7 @@ static inline void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae) {
                            "\t\t\"exec_run\":%s,\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"
@@ -1511,7 +1546,9 @@ static inline void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae) {
                            "\t\t\"non_clear_duration\":%lu,\n"
                            "\t\t\"status\":\"%s\",\n"
                            "\t\t\"old_status\":\"%s\",\n",
-                   ae->id,
+                   ae->unique_id,
+                   ae->alarm_id,
+                   ae->alarm_event_id,
                    ae->name,
                    ae->chart,
                    ae->family,
@@ -1520,6 +1557,7 @@ static inline void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae) {
                    (ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_EXEC_RUN)?"true":"false",
                    (ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_EXEC_FAILED)?"true":"false",
                    ae->exec?ae->exec:health_default_exec,
+                   ae->recipient?ae->recipient:health_default_recipient,
                    ae->exec_code,
                    ae->source,
                    ae->units?ae->units:"",
@@ -1568,6 +1606,7 @@ static inline void health_rrdcalc2json_nolock(BUFFER *wb, RRDCALC *rc) {
                    "\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"
@@ -1582,6 +1621,7 @@ static inline void health_rrdcalc2json_nolock(BUFFER *wb, RRDCALC *rc) {
             , (rc->rrdset && rc->rrdset->family)?rc->rrdset->family:""
             , (rc->rrdset)?"true":"false"
             , rc->exec?rc->exec:health_default_exec
+            , rc->recipient?rc->recipient:health_default_recipient
             , rc->source
             , rc->units?rc->units:""
             , rc->info?rc->info:""
@@ -1729,7 +1769,7 @@ static inline int rrdcalc_value2status(calculated_number n) {
     return RRDCALC_STATUS_CLEAR;
 }
 
-static inline void health_alarm_execute(ALARM_ENTRY *ae) {
+static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) {
     if(ae->old_status == RRDCALC_STATUS_UNINITIALIZED && ae->new_status == RRDCALC_STATUS_CLEAR)
         return;
 
@@ -1739,8 +1779,17 @@ static inline void health_alarm_execute(ALARM_ENTRY *ae) {
     const char *exec = ae->exec;
     if(!exec) exec = health_default_exec;
 
-    snprintfz(buffer, FILENAME_MAX, "exec %s '%s' '%s' '%s' '%s' '%s' '%0.0Lf' '%0.0Lf' '%s' '%u' '%u' '%s' '%s'",
+    const char *recipient = ae->recipient;
+    if(!recipient) recipient = health_default_recipient;
+
+    snprintfz(buffer, FILENAME_MAX, "exec %s '%s' '%s' '%u' '%u' '%u' '%zu' '%s' '%s' '%s' '%s' '%s' '%0.0Lf' '%0.0Lf' '%s' '%u' '%u' '%s' '%s'",
               exec,
+              recipient,
+              host->hostname,
+              ae->unique_id,
+              ae->alarm_id,
+              ae->alarm_event_id,
+              ae->when,
               ae->name,
               ae->chart?ae->chart:"NOCAHRT",
               ae->family?ae->family:"NOFAMILY",
@@ -1774,7 +1823,7 @@ static inline void health_alarm_execute(ALARM_ENTRY *ae) {
         ae->notifications |= HEALTH_ENTRY_NOTIFICATIONS_EXEC_FAILED;
 }
 
-static inline void health_process_notifications(ALARM_ENTRY *ae) {
+static inline void health_process_notifications(RRDHOST *host, ALARM_ENTRY *ae) {
     info("Health alarm '%s.%s' = %0.2Lf - changed status from %s to %s",
          ae->chart?ae->chart:"NOCHART", ae->name,
          ae->new_value,
@@ -1782,12 +1831,14 @@ static inline void health_process_notifications(ALARM_ENTRY *ae) {
          rrdcalc_status2string(ae->new_status)
     );
 
-    health_alarm_execute(ae);
+    health_alarm_execute(host, ae);
 }
 
-static inline void health_alarm_log(RRDHOST *host, time_t when,
+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, time_t duration,
+                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,
@@ -1807,11 +1858,14 @@ static inline void health_alarm_log(RRDHOST *host, time_t when,
         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->id = host->health_log.nextid++;
+    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;
@@ -1863,17 +1917,17 @@ static inline void health_alarm_log_process(RRDHOST *host) {
     pthread_rwlock_rdlock(&host->health_log.alarm_log_rwlock);
 
     for(ae = host->health_log.alarms; ae ;ae = ae->next) {
-        if(last_processed >= ae->id) break;
+        if(last_processed >= ae->unique_id) break;
 
         if(!(ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_PROCESSED) &&
                 !(ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_UPDATED)) {
             ae->notifications |= HEALTH_ENTRY_NOTIFICATIONS_PROCESSED;
-            health_process_notifications(ae);
+            health_process_notifications(host, ae);
         }
     }
 
     if(host->health_log.alarms)
-        last_processed = host->health_log.alarms->id;
+        last_processed = host->health_log.alarms->unique_id;
 
     pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock);
 
@@ -1899,6 +1953,7 @@ static inline void health_alarm_log_process(RRDHOST *host) {
         freez(ae->chart);
         freez(ae->family);
         freez(ae->exec);
+        freez(ae->recipient);
         freez(ae->source);
         freez(ae->units);
         freez(ae->info);
@@ -2175,7 +2230,7 @@ void *health_main(void *ptr) {
                 }
 
                 if(status != rc->status) {
-                    health_alarm_log(&localhost, time(NULL), rc->name, rc->rrdset->id, rc->rrdset->family, rc->exec, now - rc->last_status_change, rc->old_value, rc->value, rc->status, status, rc->source, rc->units, rc->info);
+                    health_alarm_log(&localhost, rc->id, rc->next_event_id++, time(NULL), 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->last_status_change = now;
                     rc->status = status;
                 }
index ef1158a29812bdead936cd69de4028ee88318232..567e1153974b233c4ff3e001c0fb3bf5bb63f68d 100644 (file)
@@ -120,10 +120,14 @@ typedef struct rrddimvar {
 #define RRDCALC_FLAG_CRIT_ERROR    0x00000020
 
 typedef struct rrdcalc {
+    uint32_t id;
+    uint32_t next_event_id;
+
     char *name;
     uint32_t hash;
 
     char *exec;
+    char *recipient;
 
     char *chart;        // the chart id this should be linked to
     uint32_t hash_chart;
@@ -182,6 +186,7 @@ typedef struct rrdcalctemplate {
     uint32_t hash_name;
 
     char *exec;
+    char *recipient;
 
     char *context;
     uint32_t hash_context;
@@ -216,7 +221,9 @@ typedef struct rrdcalctemplate {
 #define HEALTH_ENTRY_NOTIFICATIONS_EXEC_FAILED  0x00000008
 
 typedef struct alarm_entry {
-    uint32_t id;
+    uint32_t unique_id;
+    uint32_t alarm_id;
+    uint32_t alarm_event_id;
 
     time_t when;
     time_t duration;
@@ -231,6 +238,7 @@ typedef struct alarm_entry {
     char *family;
 
     char *exec;
+    char *recipient;
     int exec_code;
 
     char *source;
@@ -249,7 +257,8 @@ typedef struct alarm_entry {
 } ALARM_ENTRY;
 
 typedef struct alarm_log {
-    uint32_t nextid;
+    uint32_t next_log_id;
+    uint32_t next_alarm_id;
     unsigned int count;
     unsigned int max;
     ALARM_ENTRY *alarms;
index a751e291d68c3a502861a616277a1a36c29c4295..3db28e4f81e90e5d0783fca789ca525076dcb5e7 100644 (file)
@@ -276,6 +276,7 @@ static const char *verify_required_directory(const char *dir) {
 
 int main(int argc, char **argv)
 {
+    char *hostname = "localhost";
     int i, check_config = 0;
     int config_loaded = 0;
     int dont_fork = 0;
@@ -646,6 +647,11 @@ int main(int argc, char **argv)
             info("Successfully set pthread stacksize to %zu bytes", wanted_stacksize);
     }
 
+    // ------------------------------------------------------------------------
+    // initialize rrd host
+
+    rrdhost_init(hostname);
+
     // ------------------------------------------------------------------------
     // initialize the registry
 
index d64a9437ed331227643681df2e2d38e3f1a038cd..1b8a71442d6633075f766aecc6910b89ea299542 100644 (file)
@@ -1652,7 +1652,7 @@ int registry_init(void) {
     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", hostname));
+    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);
 
     setenv("NETDATA_REGISTRY_HOSTNAME", registry.hostname, 1);
index 61f99eda8eba99674c20033f97a231d6fdc216ab..6be65c7fc8f115960da4a708907660da1923e48f 100644 (file)
--- a/src/rrd.c
+++ b/src/rrd.c
@@ -42,7 +42,8 @@ RRDHOST localhost = {
             AVL_LOCK_INITIALIZER
         },
         .health_log = {
-            .nextid = 1,
+            .next_log_id = 1,
+            .next_alarm_id = 1,
             .count = 0,
             .max = 1000,
             .alarms = NULL,
@@ -50,6 +51,12 @@ RRDHOST localhost = {
         }
 };
 
+void rrdhost_init(char *hostname) {
+    localhost.hostname = hostname;
+    localhost.health_log.next_log_id =
+        localhost.health_log.next_alarm_id = time(NULL);
+}
+
 void rrdhost_rwlock(RRDHOST *host) {
     pthread_rwlock_wrlock(&host->rrdset_root_rwlock);
 }
index 108df0ce71d649e1626c7f33665bb51125909d56..92a65fe8a7beb77a54032480f12cf54390e4b541 100644 (file)
--- a/src/rrd.h
+++ b/src/rrd.h
@@ -310,6 +310,7 @@ struct rrdhost {
 };
 typedef struct rrdhost RRDHOST;
 extern RRDHOST localhost;
+extern void rrdhost_init(char *hostname);
 
 #ifdef NETDATA_INTERNAL_CHECKS
 #define rrdhost_check_wrlock(host) rrdhost_check_wrlock_int(host, __FILE__, __FUNCTION__, __LINE__)
index 9009a8b1dac04e9688e793834fa7c31886d6f5f4..62ce949734557120d66c2ce1376b6fa7d7d92d50 100644 (file)
@@ -1,8 +1,5 @@
 #include "common.h"
 
-#define HOSTNAME_MAX 1024
-char *hostname = "unknown";
-
 void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb)
 {
     pthread_rwlock_rdlock(&st->rwlock);
@@ -84,7 +81,7 @@ void rrd_stats_api_v1_charts(BUFFER *wb)
         ",\n\t\"update_every\": %d"
         ",\n\t\"history\": %d"
         ",\n\t\"charts\": {"
-        , hostname
+        , localhost.hostname
         , rrd_update_every
         , rrd_default_history_entries
         );
@@ -246,7 +243,7 @@ void rrd_stats_all_json(BUFFER *wb)
         "\t\"history\": %d,\n"
         "\t\"memory\": %lu\n"
         "}\n"
-        , hostname
+        , localhost.hostname
         , rrd_update_every
         , rrd_default_history_entries
         , memory
index 40c16725bd8f44a439a14ba5392e973f347dcee1..d7863dc7be3da63b99b8b25e3f0c5428f5abf866 100644 (file)
             a1 += '<li role="separator" class="divider"></li>';
 
             el += '<li><a href="https://github.com/firehol/netdata/wiki/mynetdata-menu-item" style="color: #999;" target="_blank">What is this?</a></li>';
-            a1 += '<li><a href="#" style="color: #999;" onclick="switchRegistryModalHandler(); return false;"><i class="fa fa-cog" aria-hidden="true" style="color: #999;"></i></a></li>'
+            a1 += '<li><a href="#" style="color: #999;" onclick="switchRegistryModalHandler(); return false;"><i class="fa fa-sliders" aria-hidden="true" style="color: #999;"></i></a></li>'
 
             document.getElementById('mynetdata_servers').innerHTML = el;
             document.getElementById('mynetdata_servers2').innerHTML = el;
             <nav class="collapse navbar-collapse navbar-right" role="navigation">
                 <ul class="nav navbar-nav">
                     <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#alarmsModal" title="alarms"><i class="fa fa-bell"></i><span id="alarms_count_badge" class="badge"></span></a></li>
-                    <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#optionsModal" title="dashboard settings"><i class="fa fa-cog"></i></a></li>
+                    <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#optionsModal" title="dashboard settings"><i class="fa fa-sliders"></i></a></li>
                     <li class="hidden-sm"><a href="https://github.com/firehol/netdata/wiki" class="btn" target="_blank" title="netdata community"><i class="fa fa-github"></i></a></li>
                     <li class="hidden-sm" id="updateButton"><a href="#" class="btn" data-toggle="modal" data-target="#updateModal" title="check for update"><i class="fa fa-cloud-download"></i><span id="update_badge" class="badge"></span></a></li>
                     <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#helpModal" title="dashboard help"><i class="fa fa-question-circle"></i></a></li>
                     <li class="dropdown hidden-md hidden-lg hidden-xs">
                         <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="current_view">Menu <strong class="caret"></strong></a>
                         <ul class="dropdown-menu scrollable-menu inpagemenu" role="menu">
-                            <li><a href="#" class="btn" data-toggle="modal" data-target="#alarmsModal"><i class="fa fa-cog"></i> alarms</a></li>
-                            <li><a href="#" class="btn" data-toggle="modal" data-target="#optionsModal"><i class="fa fa-cog"></i> settings</a></li>
+                            <li><a href="#" class="btn" data-toggle="modal" data-target="#alarmsModal"><i class="fa fa-bell"></i> alarms</a></li>
+                            <li><a href="#" class="btn" data-toggle="modal" data-target="#optionsModal"><i class="fa fa-sliders"></i> settings</a></li>
                             <li><a href="https://github.com/firehol/netdata/wiki" class="btn" target="_blank"><i class="fa fa-github"></i> community</a></li>
                             <li><a href="#" class="btn" data-toggle="modal" data-target="#helpModal"><i class="fa fa-question-circle"></i> help</a></li>
                         </ul>
@@ -1441,89 +1441,106 @@ function gaugeChart(title, width, dimensions, colors) {
 var menuData = {
     'system': {
         title: 'System Overview',
+        icon: '<i class="fa fa-bookmark" aria-hidden="true"></i>',
         info: 'Overview of the key system metrics.'
     },
 
     'ap': {
         title: 'Access Points',
+        icon: '<i class="fa fa-wifi" aria-hidden="true"></i>',
         info: undefined
     },
 
     'tc': {
         title: 'Quality of Service',
+        icon: '<i class="fa fa-globe" aria-hidden="true"></i>',
         info: 'Netdata collects and visualizes tc class utilization using its <a href="https://github.com/firehol/netdata/blob/master/plugins.d/tc-qos-helper.sh" target="_blank">tc-helper plugin</a>. If you also use <a href="http://firehol.org/#fireqos" target="_blank">FireQOS</a> for setting up QoS, netdata automatically collects interface and class names. If your QoS configuration includes overheads calculation, the values shown here will include these overheads (the total bandwidth for the same interface as reported in the Network Interfaces section, will be lower than the total bandwidth reported here). Also, data collection may have a slight time difference compared to the interface (QoS data collection is implemented with a BASH script, so a shift in data collection of a few milliseconds should be justified).'
     },
 
     'net': {
         title: 'Network Interfaces',
+        icon: '<i class="fa fa-share-alt" aria-hidden="true"></i>',
         info: 'Per network interface statistics collected from <code>/proc/net/dev</code>.'
     },
 
     'ipv4': {
         title: 'IPv4 Networking',
+        icon: '<i class="fa fa-cloud" aria-hidden="true"></i>',
         info: undefined
     },
 
     'ipv6': {
         title: 'IPv6 Networking',
+        icon: '<i class="fa fa-cloud" aria-hidden="true"></i>',
         info: undefined
     },
 
     'ipvs': {
         title: 'IP Virtual Server',
+        icon: '<i class="fa fa-eye" aria-hidden="true"></i>',
         info: undefined
     },
 
     'netfilter': {
         title: 'Firewall (netfilter)',
+        icon: '<i class="fa fa-shield" aria-hidden="true"></i>',
         info: undefined
     },
 
     'cpu': {
         title: 'CPUs',
+        icon: '<i class="fa fa-bolt" aria-hidden="true"></i>',
         info: undefined
     },
 
     'mem': {
         title: 'Memory',
+        icon: '<i class="fa fa-bolt" aria-hidden="true"></i>',
         info: undefined
     },
 
     'disk': {
         title: 'Disks',
+        icon: '<i class="fa fa-folder" aria-hidden="true"></i>',
         info: 'Charts with performance information for all the system disks. Special care has been given to present disk performance metrics in a way compatible with <code>iostat -x</code>. netdata by default prevents rendering performance charts for individual partitions and unmounted virtual disks. Disabled charts can still be enabled by altering the relative settings in the netdata configuration file.'
     },
 
     'sensors': {
         title: 'Sensors',
+        icon: '<i class="fa fa-leaf" aria-hidden="true"></i>',
         info: undefined
     },
 
     'nfsd': {
         title: 'File Server (nfsd)',
+        icon: '<i class="fa fa-folder-open" aria-hidden="true"></i>',
         info: undefined
     },
 
     'apps': {
         title: 'Applications',
+        icon: '<i class="fa fa-heartbeat" aria-hidden="true"></i>',
         info: 'Per application statistics are collected using netdata\'s <code>apps.plugin</code>. This plugin walks through the entire <code>/proc</code> filesystem and aggregates statistics for applications of interest, defined in <code>/etc/netdata/apps_groups.conf</code> (the default is <a href="https://github.com/firehol/netdata/blob/master/conf.d/apps_groups.conf" target="_blank">here</a>). The plugin internally builds a process tree (much like <code>ps fax</code> does), and groups processes together (evaluating both child and parent processes) so that the result is always a chart with a predefined set of dimensions (of course, only application groups found running are reported). The reported values are compatible with <code>top</code>, although the netdata plugin counts also the resources of exited children (unlike <code>top</code> which shows only the resources of the currently running processes). So for processes like shell scripts, the reported values include the resources used by the commands these scripts run within each timeframe.',
         height: 1.5
     },
 
     'users': {
         title: 'Users',
+        icon: '<i class="fa fa-user" aria-hidden="true"></i>',
         info: 'Per user statistics are collected using netdata\'s <code>apps.plugin</code>. This plugin walks through the entire <code>/proc</code> filesystem and aggregates statistics per user. The reported values are compatible with <code>top</code>, although the netdata plugin counts also the resources of exited children (unlike <code>top</code> which shows only the resources of the currently running processes). So for processes like shell scripts, the reported values include the resources used by the commands these scripts run within each timeframe.',
         height: 1.5
     },
 
     'groups': {
         title: 'User Groups',
+        icon: '<i class="fa fa-users" aria-hidden="true"></i>',
         info: 'Per user group statistics are collected using netdata\'s <code>apps.plugin</code>. This plugin walks through the entire <code>/proc</code> filesystem and aggregates statistics per user group. The reported values are compatible with <code>top</code>, although the netdata plugin counts also the resources of exited children (unlike <code>top</code> which shows only the resources of the currently running processes). So for processes like shell scripts, the reported values include the resources used by the commands these scripts run within each timeframe.',
         height: 1.5
     },
 
     'netdata': {
         title: 'Netdata Monitoring',
+        icon: '<i class="fa fa-bar-chart" aria-hidden="true"></i>',
         info: undefined
     },
 
@@ -1533,52 +1550,92 @@ var menuData = {
     },
 
     'cgroup': {
-        title: 'Container',
+        title: '',
+        icon: '<i class="fa fa-th" aria-hidden="true"></i>',
         info: undefined
     },
 
     'cgqemu': {
-        title: 'VM',
+        title: '',
+        icon: '<i class="fa fa-th-large" aria-hidden="true"></i>',
         info: undefined
     },
 
     'memcached': {
         title: 'memcached',
+        icon: '<i class="fa fa-database" aria-hidden="true"></i>',
         info: undefined
     },
 
     'mysql': {
         title: 'MySQL',
+        icon: '<i class="fa fa-database" aria-hidden="true"></i>',
         info: undefined
     },
 
     'redis': {
         title: 'Redis',
+        icon: '<i class="fa fa-database" aria-hidden="true"></i>',
         info: undefined
     },
 
     'ipfs': {
         title: 'IPFS',
+        icon: '<i class="fa fa-folder-open" aria-hidden="true"></i>',
         info: undefined
     },
 
     'phpfpm': {
         title: 'PHP-FPM',
+        icon: '<i class="fa fa-eye" aria-hidden="true"></i>',
+        info: undefined,
+    },
+
+    'postfix': {
+        title: 'postfix',
+        icon: '<i class="fa fa-envelope" aria-hidden="true"></i>',
         info: undefined,
     },
 
     'nginx': {
         title: 'nginx',
+        icon: '<i class="fa fa-eye" aria-hidden="true"></i>',
         info: undefined,
     },
 
     'apache': {
         title: 'Apache',
+        icon: '<i class="fa fa-eye" aria-hidden="true"></i>',
         info: undefined,
     },
 
     'named': {
         title: 'named',
+        icon: '<i class="fa fa-tag" aria-hidden="true"></i>',
+        info: undefined
+    },
+
+    'squid': {
+        title: 'squid',
+        icon: '<i class="fa fa-exchange" aria-hidden="true"></i>',
+        info: undefined
+    },
+
+    'nut': {
+        title: 'UPS',
+        icon: '<i class="fa fa-battery-half" aria-hidden="true"></i>',
+        info: undefined
+    },
+
+    'smawebbox': {
+        title: 'Solar Power',
+        icon: '<i class="fa fa-sun-o" aria-hidden="true"></i>',
+        info: undefined
+    },
+
+    'snmp': {
+        title: 'SNMP',
+        icon: '<i class="fa fa-random" aria-hidden="true"></i>',
         info: undefined
     }
 };
@@ -2005,13 +2062,31 @@ function anyAttribute(obj, attr, key, def) {
     return def;
 }
 
+function anyAttributeTitle(obj, attr, key, def) {
+    if(typeof obj[key] !== 'undefined') {
+        if(typeof obj[key][attr] !== 'undefined')
+            return obj[key][attr];
+    }
+
+    var r = def.replace(/_/g, ' ');
+
+    return r;
+}
+
 function menuTitle(chart) {
     if(typeof chart.menu_pattern !== 'undefined') {
-        return anyAttribute(menuData, 'title', chart.menu_pattern, chart.menu_pattern).toString()
-                + ': ' + chart.type.slice(-(chart.type.length - chart.menu_pattern.length - 1)).toString();
+        return anyAttributeTitle(menuData, 'title', chart.menu_pattern, chart.menu_pattern).toString()
+                + ' ' + chart.type.slice(-(chart.type.length - chart.menu_pattern.length - 1)).toString();
     }
 
-    return anyAttribute(menuData, 'title', chart.menu, chart.menu);
+    return anyAttributeTitle(menuData, 'title', chart.menu, chart.menu);
+}
+
+function menuIcon(chart) {
+    if(typeof chart.menu_pattern !== 'undefined')
+        return anyAttribute(menuData, 'icon', chart.menu_pattern, '<i class="fa fa-puzzle-piece" aria-hidden="true"></i>').toString();
+
+    return anyAttribute(menuData, 'icon', chart.menu, '<i class="fa fa-puzzle-piece" aria-hidden="true"></i>');
 }
 
 function menuInfo(menu) {
@@ -2024,7 +2099,13 @@ function menuHeight(menu, relative) {
 
 function submenuTitle(menu, submenu) {
     var key = menu + '.' + submenu;
-    return anyAttribute(submenuData, 'title', key, submenu);
+    var title = anyAttributeTitle(submenuData, 'title', key, submenu);
+    if(title.length > 30) {
+        var a = title.substring(0, 13);
+        var b = title.substring(title.length - 13, title.length);
+        return a + '...' + b;
+    }
+    return title;
 }
 
 function submenuInfo(menu, submenu) {
@@ -2081,10 +2162,12 @@ function enrichChartData(chart) {
         case 'mysql':
         case 'named':
         case 'nginx':
+        case 'nut':
         case 'phpfpm':
         case 'postfix':
         case 'redis':
         case 'ipfs':
+        case 'smawebbox':
         case 'squid':
         case 'snmp':
         case 'tomcat':
@@ -2284,7 +2367,7 @@ function renderPage(menus, data) {
         // generate an entry at the main menu
 
         var menuid = name2id(menu);
-        sidebar += '<li class=""><a href="#' + menuid + '" onClick="return scrollToId(\'' + menuid + '\');">' + menus[menu].title + '</a><ul class="nav">';
+        sidebar += '<li class=""><a href="#' + menuid + '" onClick="return scrollToId(\'' + menuid + '\');">' + menus[menu].icon + ' ' + menus[menu].title + '</a><ul class="nav">';
         html += '<div role="section"><div role="sectionhead"><h1 id="' + menuid + '" role="heading">' + menus[menu].title + '</h1></div><div id="menu_' + menuid + '" role="document">';
 
         if(menus[menu].info !== null)
@@ -2366,6 +2449,7 @@ function renderChartsAndMenu(data) {
                 priority: charts[c].priority,
                 submenus: {},
                 title: menuTitle(charts[c]),
+                icon: menuIcon(charts[c]),
                 info: menuInfo(charts[c].menu),
                 height: menuHeight(charts[c].menu, options.chartsHeight)
             };
@@ -2409,6 +2493,8 @@ function renderChartsAndMenu(data) {
     renderPage(menus, data);
 }
 
+// ----------------------------------------------------------------------------
+
 function alarmsUpdateModal() {
     var active = '<h3>Raised Alarms</h3><table class="table">';
     var all = '<h3>All Running Alarms</h3><div class="panel-group" id="alarms_all_accordion" role="tablist" aria-multiselectable="true">';
@@ -2491,7 +2577,7 @@ function alarmsUpdateModal() {
         function alarm_to_html(alarm, full) {
             var chart = options.data.charts[alarm.chart];
 
-            var html = '<tr><td class="text-center" style="vertical-align:middle" width="40%"><b>' + alarm.chart + '</b><br/>&nbsp;<br/><embed src="' + NETDATA.alarms.server + '/api/v1/badge.svg?chart=' + alarm.chart + '&alarm=' + alarm.name + '&refresh=auto" type="image/svg+xml" height="20" /><br/>&nbsp;<br/><span style="font-size: 18px">' + alarm.info + '</span></td>'
+            var html = '<tr><td class="text-center" style="vertical-align:middle" width="40%"><b>' + alarm.chart + '</b><br/>&nbsp;<br/><embed src="' + NETDATA.alarms.server + '/api/v1/badge.svg?chart=' + alarm.chart + '&alarm=' + alarm.name + '&refresh=auto" type="image/svg+xml" height="20" /><br/>&nbsp;<br/><span style="font-size: 18px">' + alarm.info + '</span><br/>&nbsp;<br/>role: <b>' + alarm.recipient + '</b></td>'
                 + '<td><table class="table">'
                 + ((typeof alarm.warn !== 'undefined')?('<tr><td width="10%" style="text-align:right">warning&nbsp;when</td><td><span style="font-family: monospace; color:#fe7d37; font-weight: bold;">' + alarm.warn + '</span></td></tr>'):'')
                 + ((typeof alarm.crit !== 'undefined')?('<tr><td width="10%" style="text-align:right">critical&nbsp;when</td><td><span style="font-family: monospace; color: #e05d44; font-weight: bold;">' + alarm.crit + '</span></td></tr>'):'');
@@ -2603,7 +2689,7 @@ function alarmsUpdateModal() {
                 if(alarm.status === 'WARNING' || alarm.status === 'CRITICAL') {
                     if(!active_family_added) {
                         active_family_added = true;
-                        active += '<tr><th class="text-center"><h4>' + family + '</h4></th><th></th></tr>';
+                        active += '<tr><th class="text-center" colspan="2"><h4>' + family + '</h4></th></tr>';
                     }
                     count_active++;
                     active += alarm_to_html(alarm, false);
@@ -3007,7 +3093,7 @@ function finalizePage() {
     /* activate bootstrap scrollspy (needed for sidebar) */
     $(document.body).scrollspy({
         target: '#sidebar',
-        offset: $(window).height() / 3 // controls the diff of the <hX> element to the top, to select it
+        offset: $(window).height() / 5 // controls the diff of the <hX> element to the top, to select it
     });
 
     // change the URL based on the current position of the screen