]> arthur.barton.de Git - netdata.git/blobdiff - plugins.d/alarm-notify.sh
Merge pull request #1304 from rlefevre/unknown-interrupts
[netdata.git] / plugins.d / alarm-notify.sh
index acd44923f9d768f939b4ef0d51e7a5940cb116aa..2376a8b2532c611203418b76443b223b0dcacc0b 100755 (executable)
 #
 # Supported notification methods:
 #  - emails
-#  - pushover.net notifications
 #  - slack.com notifications
-#  - telegram.org notifications
-#
+#  - pushover.net notifications
+#  - pushbullet.com push notifications by Tiago Peralta @tperalta82 PR #1070
+#  - telegram.org notifications by @hashworks PR #1002
+#  - twilio.com notifications by Levi Blaney @shadycuz PR #1211
+#  - kafka notifications
+
+# -----------------------------------------------------------------------------
+# testing notifications
+
+if [ \( "${1}" = "test" -o "${2}" = "test" \) -a "${#}" -le 2 ]
+then
+    if [ "${2}" = "test" ]
+    then
+        recipient="${1}"
+    else
+        recipient="${2}"
+    fi
+
+    [ -z "${recipient}" ] && recipient="sysadmin"
+
+    id=1
+    last="CLEAR"
+    for x in "CRITICAL" "WARNING" "CLEAR"
+    do
+        echo >&2
+        echo >&2 "# SENDING TEST ${x} ALARM TO ROLE: ${recipient}"
+
+        "${0}" "${recipient}" "$(hostname)" 1 1 "${id}" "$(date +%s)" "test_alarm" "test.chart" "test.family" "${x}" "${last}" 100 90 "${0}" 1 $((0 + id)) "units" "this is a test alarm to verify notifications work"
+        if [ $? -ne 0 ]
+        then
+            echo >&2 "# FAILED"
+        else
+            echo >&2 "# OK"
+        fi
+
+        last="${x}"
+        id=$((id + 1))
+    done
+
+    exit 1
+fi
 
 export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/sbin"
 export LC_ALL=C
@@ -58,7 +96,7 @@ fatal() {
 
 debug=0
 debug() {
-    [ $debug -eq 1 ] && log DEBUG "${@}"
+    [ ${debug} -eq 1 ] && log DEBUG "${@}"
 }
 
 # -----------------------------------------------------------------------------
@@ -132,8 +170,11 @@ sendmail=
 # enable / disable features
 SEND_SLACK="YES"
 SEND_PUSHOVER="YES"
+SEND_TWILIO="YES"
 SEND_TELEGRAM="YES"
 SEND_EMAIL="YES"
+SEND_PUSHBULLET="YES"
+SEND_KAFKA="YES"
 
 # slack configs
 SLACK_WEBHOOK_URL=
@@ -145,11 +186,27 @@ PUSHOVER_APP_TOKEN=
 DEFAULT_RECIPIENT_PUSHOVER=
 declare -A role_recipients_pushover=()
 
+# pushbullet configs
+PUSHBULLET_ACCESS_TOKEN=
+DEFAULT_RECIPIENT_PUSHBULLET=
+declare -A role_recipients_pushbullet=()
+
+# twilio configs
+TWILIO_ACCOUNT_SID=
+TWILIO_ACCOUNT_TOKEN=
+TWILIO_NUMBER=
+DEFAULT_RECIPIENT_TWILIO=
+declare -A role_recipients_twilio=()
+
 # telegram configs
 TELEGRAM_BOT_TOKEN=
 DEFAULT_RECIPIENT_TELEGRAM=
 declare -A role_recipients_telegram=()
 
+# kafka configs
+KAFKA_URL=
+KAFKA_SENDER_IP=
+
 # email configs
 DEFAULT_RECIPIENT_EMAIL="root"
 declare -A role_recipients_email=()
@@ -206,6 +263,8 @@ filter_recipient_by_criticality() {
 
 declare -A arr_slack=()
 declare -A arr_pushover=()
+declare -A arr_pushbullet=()
+declare -A arr_twilio=()
 declare -A arr_telegram=()
 declare -A arr_email=()
 
@@ -233,6 +292,22 @@ do
         [ "${r}" != "disabled" ] && filter_recipient_by_criticality pushover "${r}" && arr_pushover[${r/|*/}]="1"
     done
 
+    # pushbullet
+    a="${role_recipients_pushbullet[${x}]}"
+    [ -z "${a}" ] && a="${DEFAULT_RECIPIENT_PUSHBULLET}"
+    for r in ${a//,/ }
+    do
+        [ "${r}" != "disabled" ] && filter_recipient_by_criticality pushbullet "${r}" && arr_pushbullet[${r/|*/}]="1"
+    done
+
+    # twilio
+    a="${role_recipients_twilio[${x}]}"
+    [ -z "${a}" ] && a="${DEFAULT_RECIPIENT_TWILIO}"
+    for r in ${a//,/ }
+    do
+        [ "${r}" != "disabled" ] && filter_recipient_by_criticality twilio "${r}" && arr_twilio[${r/|*/}]="1"
+    done
+
     # telegram
     a="${role_recipients_telegram[${x}]}"
     [ -z "${a}" ] && a="${DEFAULT_RECIPIENT_TELEGRAM}"
@@ -258,6 +333,14 @@ to_slack="${!arr_slack[*]}"
 to_pushover="${!arr_pushover[*]}"
 [ -z "${to_pushover}" ] && SEND_PUSHOVER="NO"
 
+# build the list of pushbulet recipients (user tokens)
+to_pushbullet="${!arr_pushbullet[*]}"
+[ -z "${to_pushbullet}" ] && SEND_PUSHBULLET="NO"
+
+# build the list of twilio recipients (phone numbers)
+to_twilio="${!arr_twilio[*]}"
+[ -z "${to_twilio}" ] && SEND_TWILIO="NO"
+
 # check array of telegram recipients (chat ids)
 to_telegram="${!arr_telegram[*]}"
 [ -z "${to_telegram}" ] && SEND_TELEGRAM="NO"
@@ -281,20 +364,36 @@ done
 # check pushover
 [ -z "${PUSHOVER_APP_TOKEN}" ] && SEND_PUSHOVER="NO"
 
+# check pushbullet
+[ -z "${PUSHBULLET_ACCESS_TOKEN}" ] && SEND_PUSHBULLET="NO"
+
+# check twilio
+[ -z "${TWILIO_ACCOUNT_TOKEN}" -o -z "${TWILIO_ACCOUNT_SID}" -o -z "${TWILIO_NUMBER}" ] && SEND_TWILIO="NO"
+
 # check telegram
 [ -z "${TELEGRAM_BOT_TOKEN}" ] && SEND_TELEGRAM="NO"
 
-if [ \( "${SEND_PUSHOVER}" = "YES" -o "${SEND_SLACK}" = "YES" -o "${SEND_TELEGRAM}" = "YES" \) -a -z "${curl}" ]
+# check kafka
+[ -z "${KAFKA_URL}" -o -z "${KAFKA_SENDER_IP}" ] && SEND_KAFKA="NO"
+
+# if we need curl, check for the curl command
+if [ \( "${SEND_PUSHOVER}" = "YES" -o "${SEND_SLACK}" = "YES" -o "${SEND_TWILIO}" = "YES" -o "${SEND_TELEGRAM}" = "YES" -o "${SEND_PUSHBULLET}" = "YES" -o "${SEND_KAFKA}" = "YES" \) -a -z "${curl}" ]
     then
     curl="$(which curl 2>/dev/null || command -v curl 2>/dev/null)"
     if [ -z "${curl}" ]
         then
+        # no curl available
+        # disable all curl based methods
         SEND_PUSHOVER="NO"
+        SEND_PUSHBULLET="NO"
         SEND_TELEGRAM="NO"
         SEND_SLACK="NO"
+        SEND_TWILIO="NO"
+        SEND_KAFKA="NO"
     fi
 fi
 
+# if we need sendmail, check for the sendmail command
 if [ "${SEND_EMAIL}" = "YES" -a -z "${sendmail}" ]
     then
     sendmail="$(which sendmail 2>/dev/null || command -v sendmail 2>/dev/null)"
@@ -302,13 +401,20 @@ if [ "${SEND_EMAIL}" = "YES" -a -z "${sendmail}" ]
 fi
 
 # check that we have at least a method enabled
-if [ "${SEND_EMAIL}" != "YES" -a "${SEND_PUSHOVER}" != "YES" -a "${SEND_TELEGRAM}" != "YES" -a "${SEND_SLACK}" != "YES" ]
+if [   "${SEND_EMAIL}"      != "YES" \
+    -a "${SEND_PUSHOVER}"   != "YES" \
+    -a "${SEND_TELEGRAM}"   != "YES" \
+    -a "${SEND_SLACK}"      != "YES" \
+    -a "${SEND_TWILIO}"     != "YES" \
+    -a "${SEND_PUSHBULLET}" != "YES" \
+    -a "${SEND_KAFKA}"      != "YES" \
+    ]
     then
-    fatal "All notification methods are disabled. Not sending a notification."
+    fatal "All notification methods are disabled. Not sending notification to '${role}' for '${name}' = '${value}' of chart '${chart}' for status '${status}'."
 fi
 
 # -----------------------------------------------------------------------------
-# get the system hostname
+# find a suitable hostname to use, if netdata did not supply a hostname
 
 [ -z "${host}" ] && host="${NETDATA_HOSTNAME}"
 [ -z "${host}" ] && host="${NETDATA_REGISTRY_HOSTNAME}"
@@ -321,7 +427,7 @@ date="$(date --date=@${when} 2>/dev/null)"
 [ -z "${date}" ] && date="$(date 2>/dev/null)"
 
 # -----------------------------------------------------------------------------
-# URL encode a string
+# function to URL encode a string
 
 urlencode() {
     local string="${1}" strlen encoded pos c o
@@ -329,14 +435,14 @@ urlencode() {
     strlen=${#string}
     for (( pos=0 ; pos<strlen ; pos++ ))
     do
-        c=${string:$pos:1}
-        case "$c" in
+        c=${string:${pos}:1}
+        case "${c}" in
             [-_.~a-zA-Z0-9])
                 o="${c}"
                 ;;
 
             *)
-                printf -v o '%%%02x' "'$c"
+                printf -v o '%%%02x' "'${c}"
                 ;;
         esac
         encoded+="${o}"
@@ -347,7 +453,7 @@ urlencode() {
 }
 
 # -----------------------------------------------------------------------------
-# convert a duration in seconds, to a human readable duration
+# function to convert a duration in seconds, to a human readable duration
 # using DAYS, MINUTES, SECONDS
 
 duration4human() {
@@ -411,7 +517,7 @@ send_email() {
         "${sendmail}" -t
         ret=$?
 
-        if [ $ret -eq 0 ]
+        if [ ${ret} -eq 0 ]
         then
             info "sent email notification for: ${host} ${chart}.${name} is ${status} to '${to_email}'"
             return 0
@@ -471,6 +577,98 @@ send_pushover() {
     return 1
 }
 
+# -----------------------------------------------------------------------------
+# pushbullet sender
+
+send_pushbullet() {
+    local userapikey="${1}" recipients="${2}"  title="${3}" message="${4}" httpcode sent=0 user
+    if [ "${SEND_PUSHBULLET}" = "YES" -a ! -z "${userapikey}" -a ! -z "${recipients}" -a ! -z "${message}" -a ! -z "${title}" ]
+        then
+        #https://docs.pushbullet.com/#create-push
+        for user in ${recipients}
+        do
+            httpcode=$(${curl} --write-out %{http_code} --silent --output /dev/null \
+              --header 'Access-Token: '${userapikey}'' \
+              --header 'Content-Type: application/json' \
+              --data-binary  @<(cat <<EOF
+                              {"title": "${title}",
+                              "type": "note",
+                              "email": "${user}",
+                              "body": "$( echo -n ${message})"}
+EOF
+               ) "https://api.pushbullet.com/v2/pushes" -X POST)
+
+            if [ "${httpcode}" == "200" ]
+            then
+                info "sent pushbullet notification for: ${host} ${chart}.${name} is ${status} to '${user}'"
+                sent=$((sent + 1))
+            else
+                error "failed to send pushbullet notification for: ${host} ${chart}.${name} is ${status} to '${user}' with HTTP error code ${httpcode}."
+            fi
+        done
+
+        [ ${sent} -gt 0 ] && return 0
+    fi
+
+    return 1
+}
+
+# -----------------------------------------------------------------------------
+# kafka sender
+
+send_kafka() {
+    local httpcode sent=0 
+    if [ "${SEND_KAFKA}" = "YES" ]
+        then
+            httpcode=$(${curl} -X POST --write-out %{http_code} --silent --output /dev/null \
+                --data "{host_ip:\"${KAFKA_SENDER_IP}\",when:${when},name:\"${name}\",chart:\"${chart}\",family:\"${family}\",status:\"${status}\",old_status:\"${old_status}\",value:${value},old_value:${old_value},duration:${duration},non_clear_duration:${non_clear_duration},units:\"${units}\",info:\"${info}\"}" \
+                "${KAFKA_URL}")
+
+            if [ "${httpcode}" == "200" ]
+            then
+                info "sent kafka data for: ${host} ${chart}.${name} is ${status} and ip '${KAFKA_SENDER_IP}'"
+                sent=$((sent + 1))
+            else
+                error "failed to send kafka data for: ${host} ${chart}.${name} is ${status} and ip '${KAFKA_SENDER_IP}' with HTTP error code ${httpcode}."
+            fi
+
+        [ ${sent} -gt 0 ] && return 0
+    fi
+
+    return 1
+}
+
+# -----------------------------------------------------------------------------
+# twilio sender
+
+send_twilio() {
+    local accountsid="${1}" accounttoken="${2}" twilionumber="${3}" recipients="${4}"  title="${5}" message="${6}" httpcode sent=0 user
+    if [ "${SEND_TWILIO}" = "YES" -a ! -z "${accountsid}" -a ! -z "${accounttoken}" -a ! -z "${twilionumber}" -a ! -z "${recipients}" -a ! -z "${message}" -a ! -z "${title}" ]
+        then
+        #https://www.twilio.com/packages/labs/code/bash/twilio-sms
+        for user in ${recipients}
+        do
+            httpcode=$(${curl} -X POST --write-out %{http_code} --silent --output /dev/null \
+                --data-urlencode "From=${twilionumber}" \
+                --data-urlencode "To=${user}" \
+                --data-urlencode "Body=${title} ${message}" \
+                -u "${accountsid}:${accounttoken}" \
+                "https://api.twilio.com/2010-04-01/Accounts/${accountsid}/Messages.json")
+
+            if [ "${httpcode}" == "201" ]
+            then
+                info "sent Twilio SMS for: ${host} ${chart}.${name} is ${status} to '${user}'"
+                sent=$((sent + 1))
+            else
+                error "failed to send Twilio SMS for: ${host} ${chart}.${name} is ${status} to '${user}' with HTTP error code ${httpcode}."
+            fi
+        done
+
+        [ ${sent} -gt 0 ] && return 0
+    fi
+
+    return 1
+}
 
 # -----------------------------------------------------------------------------
 # telegram sender
@@ -488,8 +686,8 @@ send_telegram() {
             httpcode=$(${curl} --write-out %{http_code} --silent --output /dev/null ${disableNotification} \
                 --data-urlencode "parse_mode=HTML" \
                 --data-urlencode "disable_web_page_preview=true" \
-                --data-urlencode "text=$message" \
-                "https://api.telegram.org/bot${bottoken}/sendMessage?chat_id=$chatid")
+                --data-urlencode "text=${message}" \
+                "https://api.telegram.org/bot${bottoken}/sendMessage?chat_id=${chatid}")
 
             if [ "${httpcode}" == "200" ]
             then
@@ -518,10 +716,10 @@ send_slack() {
     [ "${SEND_SLACK}" != "YES" ] && return 1
 
     case "${status}" in
-        WARNING) color="warning" ;;
+        WARNING)  color="warning" ;;
         CRITICAL) color="danger" ;;
-        CLEAR) color="good" ;;
-        *) color="#777777" ;;
+        CLEAR)    color="good" ;;
+        *)        color="#777777" ;;
     esac
 
     for channel in ${channels}
@@ -622,25 +820,25 @@ case "${status}" in
         image="${images_base_url}/images/check-mark-2-128-green.png"
        status_message="recovered"
                color="#77ca6d"
-
-               # don't show the value when the status is CLEAR
-               # for certain alarms, this value might not have any meaning
-               alarm="${name//_/ } ${raised_for}"
                ;;
 esac
 
 if [ "${status}" = "CLEAR" ]
 then
     severity="Recovered from ${old_status}"
-    if [ $non_clear_duration -gt $duration ]
+    if [ ${non_clear_duration} -gt ${duration} ]
     then
         raised_for="(alarm was raised for ${non_clear_duration_txt})"
     fi
 
+    # don't show the value when the status is CLEAR
+    # for certain alarms, this value might not have any meaning
+    alarm="${name//_/ } ${raised_for}"
+
 elif [ "${old_status}" = "WARNING" -a "${status}" = "CRITICAL" ]
 then
     severity="Escalated to ${status}"
-    if [ $non_clear_duration -gt $duration ]
+    if [ ${non_clear_duration} -gt ${duration} ]
     then
         raised_for="(alarm is raised for ${non_clear_duration_txt})"
     fi
@@ -648,7 +846,7 @@ then
 elif [ "${old_status}" = "CRITICAL" -a "${status}" = "WARNING" ]
 then
     severity="Demoted to ${status}"
-    if [ $non_clear_duration -gt $duration ]
+    if [ ${non_clear_duration} -gt ${duration} ]
     then
         raised_for="(alarm is raised for ${non_clear_duration_txt})"
     fi
@@ -688,6 +886,29 @@ send_pushover "${PUSHOVER_APP_TOKEN}" "${to_pushover}" "${when}" "${goto_url}" "
 
 SENT_PUSHOVER=$?
 
+# -----------------------------------------------------------------------------
+# send the pushbullet notification
+
+send_pushbullet "${PUSHBULLET_ACCESS_TOKEN}" "${to_pushbullet}" "${host} ${status_message} - ${name//_/ } - ${chart}" "${alarm}\n
+Severity: ${severity}\n
+Chart: ${chart}\n
+Family: ${family}\n
+To View Netdata go to: ${goto_url}\n
+The source of this alarm is line ${src}"
+
+SENT_PUSHBULLET=$?
+
+# -----------------------------------------------------------------------------
+# send the twilio SMS
+
+send_twilio "${TWILIO_ACCOUNT_SID}" "${TWILIO_ACCOUNT_TOKEN}" "${TWILIO_NUMBER}" "${to_twilio}" "${host} ${status_message} - ${name//_/ } - ${chart}" "${alarm} 
+Severity: ${severity}
+Chart: ${chart}
+Family: ${family}
+${info}"
+
+SENT_TWILIO=$?
+
 # -----------------------------------------------------------------------------
 # send the telegram.org message
 
@@ -699,6 +920,14 @@ ${chart} (${family})
 
 SENT_TELEGRAM=$?
 
+
+# -----------------------------------------------------------------------------
+# send the kafka message
+
+send_kafka
+SENT_KAFKA=$?
+
+
 # -----------------------------------------------------------------------------
 # send the email
 
@@ -797,8 +1026,18 @@ SENT_EMAIL=$?
 # -----------------------------------------------------------------------------
 # let netdata know
 
-# we did send something
-[ ${SENT_EMAIL} -eq 0 -o ${SENT_PUSHOVER} -eq 0 -o ${SENT_TELEGRAM} -eq 0 -o ${SENT_SLACK} -eq 0 ] && exit 0
+if [   ${SENT_EMAIL}      -eq 0 \
+    -o ${SENT_PUSHOVER}   -eq 0 \
+    -o ${SENT_TELEGRAM}   -eq 0 \
+    -o ${SENT_SLACK}      -eq 0 \
+    -o ${SENT_TWILIO}     -eq 0 \
+    -o ${SENT_PUSHBULLET} -eq 0 \
+    -o ${SENT_KAFKA}      -eq 0 \
+    ]
+    then
+    # we did send something
+    exit 0
+fi
 
 # we did not send anything
 exit 1