alarm-notify.sh.in 80 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504
  1. #!/usr/bin/env bash
  2. #shellcheck source=/dev/null disable=SC2086,SC2154
  3. # netdata
  4. # real-time performance and health monitoring, done right!
  5. # (C) 2017 Costa Tsaousis <costa@tsaousis.gr>
  6. # SPDX-License-Identifier: GPL-3.0-or-later
  7. #
  8. # Script to send alarm notifications for netdata
  9. #
  10. # Features:
  11. # - multiple notification methods
  12. # - multiple roles per alarm
  13. # - multiple recipients per role
  14. # - severity filtering per recipient
  15. #
  16. # Supported notification methods:
  17. # - emails by @ktsaou
  18. # - slack.com notifications by @ktsaou
  19. # - alerta.io notifications by @kattunga
  20. # - discordapp.com notifications by @lowfive
  21. # - pushover.net notifications by @ktsaou
  22. # - pushbullet.com push notifications by Tiago Peralta @tperalta82 #1070
  23. # - telegram.org notifications by @hashworks #1002
  24. # - twilio.com notifications by Levi Blaney @shadycuz #1211
  25. # - kafka notifications by @ktsaou #1342
  26. # - pagerduty.com notifications by Jim Cooley @jimcooley #1373
  27. # - messagebird.com notifications by @tech_no_logical #1453
  28. # - hipchat notifications by @ktsaou #1561
  29. # - fleep notifications by @Ferroin
  30. # - prowlapp.com notifications by @Ferroin
  31. # - irc notifications by @manosf
  32. # - custom notifications by @ktsaou
  33. # - syslog messages by @Ferroin
  34. # - Microsoft Team notification by @tioumen
  35. # - RocketChat notifications by @Hermsi1337 #3777
  36. # - Google Hangouts Chat notifications by @EnzoAkira and @hendrikhofstadt
  37. # - Dynatrace Event by @illumine
  38. # -----------------------------------------------------------------------------
  39. # testing notifications
  40. if { [ "${1}" = "test" ] || [ "${2}" = "test" ]; } && [ "${#}" -le 2 ]; then
  41. if [ "${2}" = "test" ]; then
  42. recipient="${1}"
  43. else
  44. recipient="${2}"
  45. fi
  46. [ -z "${recipient}" ] && recipient="sysadmin"
  47. id=1
  48. last="CLEAR"
  49. test_res=0
  50. for x in "WARNING" "CRITICAL" "CLEAR"; do
  51. echo >&2
  52. echo >&2 "# SENDING TEST ${x} ALARM TO ROLE: ${recipient}"
  53. "${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" "new value" "old value" "evaluated expression" "expression variable values" 0 0
  54. #shellcheck disable=SC2181
  55. if [ $? -ne 0 ]; then
  56. echo >&2 "# FAILED"
  57. test_res=1
  58. else
  59. echo >&2 "# OK"
  60. fi
  61. last="${x}"
  62. id=$((id + 1))
  63. done
  64. exit $test_res
  65. fi
  66. export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/sbin"
  67. export LC_ALL=C
  68. # -----------------------------------------------------------------------------
  69. PROGRAM_NAME="$(basename "${0}")"
  70. logdate() {
  71. date "+%Y-%m-%d %H:%M:%S"
  72. }
  73. log() {
  74. local status="${1}"
  75. shift
  76. echo >&2 "$(logdate): ${PROGRAM_NAME}: ${status}: ${*}"
  77. }
  78. warning() {
  79. log WARNING "${@}"
  80. }
  81. error() {
  82. log ERROR "${@}"
  83. }
  84. info() {
  85. log INFO "${@}"
  86. }
  87. fatal() {
  88. log FATAL "${@}"
  89. exit 1
  90. }
  91. debug=${NETDATA_ALARM_NOTIFY_DEBUG-0}
  92. debug() {
  93. [ "${debug}" = "1" ] && log DEBUG "${@}"
  94. }
  95. docurl() {
  96. if [ -z "${curl}" ]; then
  97. error "${curl} is unset."
  98. return 1
  99. fi
  100. if [ "${debug}" = "1" ]; then
  101. echo >&2 "--- BEGIN curl command ---"
  102. printf >&2 "%q " ${curl} "${@}"
  103. echo >&2
  104. echo >&2 "--- END curl command ---"
  105. local out code ret
  106. out=$(mktemp /tmp/netdata-health-alarm-notify-XXXXXXXX)
  107. code=$(${curl} ${curl_options} --write-out "%{http_code}" --output "${out}" --silent --show-error "${@}")
  108. ret=$?
  109. echo >&2 "--- BEGIN received response ---"
  110. cat >&2 "${out}"
  111. echo >&2
  112. echo >&2 "--- END received response ---"
  113. echo >&2 "RECEIVED HTTP RESPONSE CODE: ${code}"
  114. rm "${out}"
  115. echo "${code}"
  116. return ${ret}
  117. fi
  118. ${curl} ${curl_options} --write-out "%{http_code}" --output /dev/null --silent --show-error "${@}"
  119. return $?
  120. }
  121. # -----------------------------------------------------------------------------
  122. # List of all the notification mechanisms we support.
  123. # Used in a couple of places to write more compact code.
  124. method_names="
  125. email
  126. pushover
  127. pushbullet
  128. telegram
  129. slack
  130. alerta
  131. flock
  132. discord
  133. hipchat
  134. twilio
  135. messagebird
  136. pd
  137. fleep
  138. syslog
  139. custom
  140. msteam
  141. kavenegar
  142. prowl
  143. irc
  144. awssns
  145. rocketchat
  146. sms
  147. hangouts
  148. dynatrace
  149. "
  150. # -----------------------------------------------------------------------------
  151. # this is to be overwritten by the config file
  152. custom_sender() {
  153. info "not sending custom notification for ${status} of '${host}.${chart}.${name}'"
  154. }
  155. # -----------------------------------------------------------------------------
  156. # check for BASH v4+ (required for associative arrays)
  157. if [ ${BASH_VERSINFO[0]} -lt 4 ]; then
  158. fatal "BASH version 4 or later is required (this is ${BASH_VERSION})."
  159. fi
  160. # -----------------------------------------------------------------------------
  161. # defaults to allow running this script by hand
  162. [ -z "${NETDATA_USER_CONFIG_DIR}" ] && NETDATA_USER_CONFIG_DIR="@configdir_POST@"
  163. [ -z "${NETDATA_STOCK_CONFIG_DIR}" ] && NETDATA_STOCK_CONFIG_DIR="@libconfigdir_POST@"
  164. [ -z "${NETDATA_CACHE_DIR}" ] && NETDATA_CACHE_DIR="@cachedir_POST@"
  165. [ -z "${NETDATA_REGISTRY_URL}" ] && NETDATA_REGISTRY_URL="https://registry.my-netdata.io"
  166. [ -z "${NETDATA_REGISTRY_CLOUD_BASE_URL}" ] && NETDATA_REGISTRY_CLOUD_BASE_URL="https://app.netdata.cloud"
  167. # -----------------------------------------------------------------------------
  168. # parse command line parameters
  169. if [[ ${1} = "unittest" ]]; then
  170. unittest=1 # enable unit testing mode
  171. roles="${2}" # the role that should be used for unit testing
  172. cfgfile="${3}" # the location of the config file to use for unit testing
  173. status="${4}" # the current status : REMOVED, UNINITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL
  174. old_status="${5}" # the previous status: REMOVED, UNINITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL
  175. else
  176. roles="${1}" # the roles that should be notified for this event
  177. args_host="${2}" # the host generated this event
  178. unique_id="${3}" # the unique id of this event
  179. alarm_id="${4}" # the unique id of the alarm that generated this event
  180. event_id="${5}" # the incremental id of the event, for this alarm id
  181. when="${6}" # the timestamp this event occurred
  182. name="${7}" # the name of the alarm, as given in netdata health.d entries
  183. chart="${8}" # the name of the chart (type.id)
  184. family="${9}" # the family of the chart
  185. status="${10}" # the current status : REMOVED, UNINITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL
  186. old_status="${11}" # the previous status: REMOVED, UNINITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL
  187. value="${12}" # the current value of the alarm
  188. old_value="${13}" # the previous value of the alarm
  189. src="${14}" # the line number and file the alarm has been configured
  190. duration="${15}" # the duration in seconds of the previous alarm state
  191. non_clear_duration="${16}" # the total duration in seconds this is/was non-clear
  192. units="${17}" # the units of the value
  193. info="${18}" # a short description of the alarm
  194. value_string="${19}" # friendly value (with units)
  195. # shellcheck disable=SC2034
  196. # variable is unused, but https://github.com/netdata/netdata/pull/5164#discussion_r255572947
  197. old_value_string="${20}" # friendly old value (with units), previously named "old_value_string"
  198. calc_expression="${21}" # contains the expression that was evaluated to trigger the alarm
  199. calc_param_values="${22}" # the values of the parameters in the expression, at the time of the evaluation
  200. total_warnings="${23}" # Total number of alarms in WARNING state
  201. total_critical="${24}" # Total number of alarms in CRITICAL state
  202. fi
  203. # -----------------------------------------------------------------------------
  204. # find a suitable hostname to use, if netdata did not supply a hostname
  205. if [ -z ${args_host} ]; then
  206. this_host=$(hostname -s 2>/dev/null)
  207. host="${this_host}"
  208. args_host="${this_host}"
  209. else
  210. host="${args_host}"
  211. fi
  212. # -----------------------------------------------------------------------------
  213. # screen statuses we don't need to send a notification
  214. # don't do anything if this is not WARNING, CRITICAL or CLEAR
  215. if [ "${status}" != "WARNING" ] && [ "${status}" != "CRITICAL" ] && [ "${status}" != "CLEAR" ]; then
  216. info "not sending notification for ${status} of '${host}.${chart}.${name}'"
  217. exit 1
  218. fi
  219. # don't do anything if this is CLEAR, but it was not WARNING or CRITICAL
  220. if [ "${clear_alarm_always}" != "YES" ] && [ "${old_status}" != "WARNING" ] && [ "${old_status}" != "CRITICAL" ] && [ "${status}" = "CLEAR" ]; then
  221. info "not sending notification for ${status} of '${host}.${chart}.${name}' (last status was ${old_status})"
  222. exit 1
  223. fi
  224. # -----------------------------------------------------------------------------
  225. # load configuration
  226. # By default fetch images from the global public registry.
  227. # This is required by default, since all notification methods need to download
  228. # images via the Internet, and private registries might not be reachable.
  229. # This can be overwritten at the configuration file.
  230. images_base_url="https://registry.my-netdata.io"
  231. # curl options to use
  232. curl_options=""
  233. # hostname handling
  234. use_fqdn="NO"
  235. # needed commands
  236. # if empty they will be searched in the system path
  237. curl=
  238. sendmail=
  239. # enable / disable features
  240. for method_name in ${method_names^^}; do
  241. declare SEND_${method_name}="YES"
  242. declare DEFAULT_RECIPIENT_${method_name}
  243. done
  244. for method_name in ${method_names}; do
  245. declare -A role_recipients_${method_name}
  246. done
  247. # slack configs
  248. SLACK_WEBHOOK_URL=
  249. # Microsoft Team configs
  250. MSTEAM_WEBHOOK_URL=
  251. # rocketchat configs
  252. ROCKETCHAT_WEBHOOK_URL=
  253. # alerta configs
  254. ALERTA_WEBHOOK_URL=
  255. ALERTA_API_KEY=
  256. # flock configs
  257. FLOCK_WEBHOOK_URL=
  258. # discord configs
  259. DISCORD_WEBHOOK_URL=
  260. # pushover configs
  261. PUSHOVER_APP_TOKEN=
  262. # pushbullet configs
  263. PUSHBULLET_ACCESS_TOKEN=
  264. PUSHBULLET_SOURCE_DEVICE=
  265. # twilio configs
  266. TWILIO_ACCOUNT_SID=
  267. TWILIO_ACCOUNT_TOKEN=
  268. TWILIO_NUMBER=
  269. # hipchat configs
  270. HIPCHAT_SERVER=
  271. HIPCHAT_AUTH_TOKEN=
  272. # messagebird configs
  273. MESSAGEBIRD_ACCESS_KEY=
  274. MESSAGEBIRD_NUMBER=
  275. # kavenegar configs
  276. KAVENEGAR_API_KEY=
  277. KAVENEGAR_SENDER=
  278. # telegram configs
  279. TELEGRAM_BOT_TOKEN=
  280. # kafka configs
  281. SEND_KAFKA="YES"
  282. KAFKA_URL=
  283. KAFKA_SENDER_IP=
  284. # pagerduty.com configs
  285. PD_SERVICE_KEY=
  286. # fleep.io configs
  287. FLEEP_SENDER="${host}"
  288. # Amazon SNS configs
  289. AWSSNS_MESSAGE_FORMAT=
  290. # syslog configs
  291. SYSLOG_FACILITY=
  292. # email configs
  293. EMAIL_SENDER=
  294. EMAIL_CHARSET=$(locale charmap 2>/dev/null)
  295. EMAIL_THREADING=
  296. EMAIL_PLAINTEXT_ONLY=
  297. # irc configs
  298. IRC_NICKNAME=
  299. IRC_REALNAME=
  300. IRC_NETWORK=
  301. # hangouts configs
  302. declare -A HANGOUTS_WEBHOOK_URI
  303. # dynatrace configs
  304. DYNATRACE_SPACE=
  305. DYNATRACE_SERVER=
  306. DYNATRACE_TOKEN=
  307. DYNATRACE_TAG_VALUE=
  308. DYNATRACE_ANNOTATION_TYPE=
  309. DYNATRACE_EVENT=
  310. SEND_DYNATRACE=
  311. # load the stock and user configuration files
  312. # these will overwrite the variables above
  313. if [ ${unittest} ]; then
  314. if source "${cfgfile}"; then
  315. error "Failed to load requested config file."
  316. exit 1
  317. fi
  318. else
  319. for CONFIG in "${NETDATA_STOCK_CONFIG_DIR}/health_alarm_notify.conf" "${NETDATA_USER_CONFIG_DIR}/health_alarm_notify.conf"; do
  320. if [ -f "${CONFIG}" ]; then
  321. debug "Loading config file '${CONFIG}'..."
  322. source "${CONFIG}" || error "Failed to load config file '${CONFIG}'."
  323. else
  324. warning "Cannot find file '${CONFIG}'."
  325. fi
  326. done
  327. fi
  328. # If we didn't autodetect the character set for e-mail and it wasn't
  329. # set by the user, we need to set it to a reasonable default. UTF-8
  330. # should be correct for almost all modern UNIX systems.
  331. if [ -z ${EMAIL_CHARSET} ]; then
  332. EMAIL_CHARSET="UTF-8"
  333. fi
  334. # If we've been asked to use FQDN's for the URL's in the alarm, do so,
  335. # unless we're sending an alarm for a slave system which we can't get the
  336. # FQDN of easily.
  337. if [ "${use_fqdn}" = "YES" ] && [ "${host}" = "$(hostname -s 2>/dev/null)" ]; then
  338. host="$(hostname -f 2>/dev/null)"
  339. fi
  340. # -----------------------------------------------------------------------------
  341. # filter a recipient based on alarm event severity
  342. filter_recipient_by_criticality() {
  343. local method="${1}" x="${2}" r s
  344. shift
  345. r="${x/|*/}" # the recipient
  346. s="${x/*|/}" # the severity required for notifying this recipient
  347. # no severity filtering for this person
  348. [ "${r}" = "${s}" ] && return 0
  349. # the severity is invalid
  350. s="${s^^}"
  351. if [ "${s}" != "CRITICAL" ]; then
  352. error "SEVERITY FILTERING for ${x} VIA ${method}: invalid severity '${s,,}', only 'critical' is supported."
  353. return 0
  354. fi
  355. # create the status tracking directory for this user
  356. [ ! -d "${NETDATA_CACHE_DIR}/alarm-notify/${method}/${r}" ] &&
  357. mkdir -p "${NETDATA_CACHE_DIR}/alarm-notify/${method}/${r}"
  358. case "${status}" in
  359. CRITICAL)
  360. # make sure he will get future notifications for this alarm too
  361. touch "${NETDATA_CACHE_DIR}/alarm-notify/${method}/${r}/${alarm_id}"
  362. debug "SEVERITY FILTERING for ${x} VIA ${method}: ALLOW: the alarm is CRITICAL (will now receive next status change)"
  363. return 0
  364. ;;
  365. WARNING)
  366. if [ -f "${NETDATA_CACHE_DIR}/alarm-notify/${method}/${r}/${alarm_id}" ]; then
  367. # we do not remove the file, so that he will get future notifications of this alarm
  368. debug "SEVERITY FILTERING for ${x} VIA ${method}: ALLOW: recipient has been notified for this alarm in the past (will still receive next status change)"
  369. fi
  370. ;;
  371. *)
  372. if [ -f "${NETDATA_CACHE_DIR}/alarm-notify/${method}/${r}/${alarm_id}" ]; then
  373. # remove the file, so that he will only receive notifications for CRITICAL states for this alarm
  374. rm "${NETDATA_CACHE_DIR}/alarm-notify/${method}/${r}/${alarm_id}"
  375. debug "SEVERITY FILTERING for ${x} VIA ${method}: ALLOW: recipient has been notified for this alarm (will only receive CRITICAL notifications from now on)"
  376. fi
  377. ;;
  378. esac
  379. debug "SEVERITY FILTERING for ${x} VIA ${method}: BLOCK: recipient should not receive this notification"
  380. return 1
  381. }
  382. # -----------------------------------------------------------------------------
  383. # verify the delivery methods supported
  384. # check slack
  385. [ -z "${SLACK_WEBHOOK_URL}" ] && SEND_SLACK="NO"
  386. # check rocketchat
  387. [ -z "${ROCKETCHAT_WEBHOOK_URL}" ] && SEND_ROCKETCHAT="NO"
  388. # check alerta
  389. [ -z "${ALERTA_WEBHOOK_URL}" ] && SEND_ALERTA="NO"
  390. # check flock
  391. [ -z "${FLOCK_WEBHOOK_URL}" ] && SEND_FLOCK="NO"
  392. # check discord
  393. [ -z "${DISCORD_WEBHOOK_URL}" ] && SEND_DISCORD="NO"
  394. # check pushover
  395. [ -z "${PUSHOVER_APP_TOKEN}" ] && SEND_PUSHOVER="NO"
  396. # check pushbullet
  397. [ -z "${PUSHBULLET_ACCESS_TOKEN}" ] && SEND_PUSHBULLET="NO"
  398. # check twilio
  399. { [ -z "${TWILIO_ACCOUNT_TOKEN}" ] || [ -z "${TWILIO_ACCOUNT_SID}" ] || [ -z "${TWILIO_NUMBER}" ]; } && SEND_TWILIO="NO"
  400. # check hipchat
  401. [ -z "${HIPCHAT_AUTH_TOKEN}" ] && SEND_HIPCHAT="NO"
  402. # check messagebird
  403. { [ -z "${MESSAGEBIRD_ACCESS_KEY}" ] || [ -z "${MESSAGEBIRD_NUMBER}" ]; } && SEND_MESSAGEBIRD="NO"
  404. # check kavenegar
  405. { [ -z "${KAVENEGAR_API_KEY}" ] || [ -z "${KAVENEGAR_SENDER}" ]; } && SEND_KAVENEGAR="NO"
  406. # check telegram
  407. [ -z "${TELEGRAM_BOT_TOKEN}" ] && SEND_TELEGRAM="NO"
  408. # check kafka
  409. { [ -z "${KAFKA_URL}" ] || [ -z "${KAFKA_SENDER_IP}" ]; } && SEND_KAFKA="NO"
  410. # check irc
  411. [ -z "${IRC_NETWORK}" ] && SEND_IRC="NO"
  412. # check hangouts
  413. [ ${#HANGOUTS_WEBHOOK_URI[@]} -eq 0 ] && SEND_HANGOUTS="NO"
  414. # check fleep
  415. #shellcheck disable=SC2153
  416. { [ -z "${FLEEP_SERVER}" ] || [ -z "${FLEEP_SENDER}" ]; } && SEND_FLEEP="NO"
  417. # check dynatrace
  418. { [ -z "${DYNATRACE_SPACE}" ] ||
  419. [ -z "${DYNATRACE_SERVER}" ] ||
  420. [ -z "${DYNATRACE_TOKEN}" ] ||
  421. [ -z "${DYNATRACE_TAG_VALUE}" ] ||
  422. [ -z "${DYNATRACE_EVENT}" ]; } && SEND_DYNATRACE="NO"
  423. if [ "${SEND_PUSHOVER}" = "YES" ] ||
  424. [ "${SEND_SLACK}" = "YES" ] ||
  425. [ "${SEND_ROCKETCHAT}" = "YES" ] ||
  426. [ "${SEND_ALERTA}" = "YES" ] ||
  427. [ "${SEND_PD}" = "YES" ] ||
  428. [ "${SEND_FLOCK}" = "YES" ] ||
  429. [ "${SEND_DISCORD}" = "YES" ] ||
  430. [ "${SEND_HIPCHAT}" = "YES" ] ||
  431. [ "${SEND_TWILIO}" = "YES" ] ||
  432. [ "${SEND_MESSAGEBIRD}" = "YES" ] ||
  433. [ "${SEND_KAVENEGAR}" = "YES" ] ||
  434. [ "${SEND_TELEGRAM}" = "YES" ] ||
  435. [ "${SEND_PUSHBULLET}" = "YES" ] ||
  436. [ "${SEND_KAFKA}" = "YES" ] ||
  437. [ "${SEND_FLEEP}" = "YES" ] ||
  438. [ "${SEND_PROWL}" = "YES" ] ||
  439. [ "${SEND_HANGOUTS}" = "YES" ] ||
  440. [ "${SEND_CUSTOM}" = "YES" ] ||
  441. [ "${SEND_MSTEAM}" = "YES" ] ||
  442. [ "${SEND_DYNATRACE}" = "YES" ]; then
  443. # if we need curl, check for the curl command
  444. if [ -z "${curl}" ]; then
  445. curl="$(command -v curl 2>/dev/null)"
  446. fi
  447. if [ -z "${curl}" ]; then
  448. error "Cannot find curl command in the system path. Disabling all curl based notifications."
  449. SEND_PUSHOVER="NO"
  450. SEND_PUSHBULLET="NO"
  451. SEND_TELEGRAM="NO"
  452. SEND_SLACK="NO"
  453. SEND_MSTEAM="NO"
  454. SEND_ROCKETCHAT="NO"
  455. SEND_ALERTA="NO"
  456. SEND_PD="NO"
  457. SEND_FLOCK="NO"
  458. SEND_DISCORD="NO"
  459. SEND_TWILIO="NO"
  460. SEND_HIPCHAT="NO"
  461. SEND_MESSAGEBIRD="NO"
  462. SEND_KAVENEGAR="NO"
  463. SEND_KAFKA="NO"
  464. SEND_FLEEP="NO"
  465. SEND_PROWL="NO"
  466. SEND_HANGOUTS="NO"
  467. SEND_CUSTOM="NO"
  468. SEND_DYNATRACE="NO"
  469. fi
  470. fi
  471. if [ "${SEND_SMS}" = "YES" ]; then
  472. if [ -z "${sendsms}" ]; then
  473. sendsms="$(command -v sendsms 2>/dev/null)"
  474. fi
  475. if [ -z "${sendsms}" ]; then
  476. SEND_SMS="NO"
  477. fi
  478. fi
  479. # if we need sendmail, check for the sendmail command
  480. if [ "${SEND_EMAIL}" = "YES" ] && [ -z "${sendmail}" ]; then
  481. sendmail="$(command -v sendmail 2>/dev/null)"
  482. if [ -z "${sendmail}" ]; then
  483. debug "Cannot find sendmail command in the system path. Disabling email notifications."
  484. SEND_EMAIL="NO"
  485. fi
  486. fi
  487. # if we need logger, check for the logger command
  488. if [ "${SEND_SYSLOG}" = "YES" ] && [ -z "${logger}" ]; then
  489. logger="$(command -v logger 2>/dev/null)"
  490. if [ -z "${logger}" ]; then
  491. debug "Cannot find logger command in the system path. Disabling syslog notifications."
  492. SEND_SYSLOG="NO"
  493. fi
  494. fi
  495. # if we need aws, check for the aws command
  496. if [ "${SEND_AWSSNS}" = "YES" ] && [ -z "${aws}" ]; then
  497. aws="$(command -v aws 2>/dev/null)"
  498. if [ -z "${aws}" ]; then
  499. debug "Cannot find aws command in the system path. Disabling Amazon SNS notifications."
  500. SEND_AWSSNS="NO"
  501. fi
  502. fi
  503. # -----------------------------------------------------------------------------
  504. # find the recipients' addresses per method
  505. # netdata may call us with multiple roles, and roles may have multiple but
  506. # overlapping recipients - so, here we find the unique recipients.
  507. for method_name in ${method_names}; do
  508. send_var="SEND_${method_name^^}"
  509. if [ "${!send_var}" = "NO" ]; then
  510. continue
  511. fi
  512. declare -A arr_var=()
  513. for x in ${roles//,/ }; do
  514. # the roles 'silent' and 'disabled' mean:
  515. # don't send a notification for this role
  516. if [ "${x}" = "silent" ] || [ "${x}" = "disabled" ]; then
  517. continue
  518. fi
  519. role_recipients="role_recipients_${method_name}[$x]"
  520. default_recipient_var="DEFAULT_RECIPIENT_${method_name^^}"
  521. a="${!role_recipients}"
  522. [ -z "${a}" ] && a="${!default_recipient_var}"
  523. for r in ${a//,/ }; do
  524. [ "${r}" != "disabled" ] && filter_recipient_by_criticality ${method_name} "${r}" && arr_var[${r/|*/}]="1"
  525. done
  526. done
  527. # build the list of recipients
  528. to_var="to_${method_name}"
  529. declare to_${method_name}="${!arr_var[*]}"
  530. [ -z "${!to_var}" ] && declare ${send_var}="NO"
  531. done
  532. # -----------------------------------------------------------------------------
  533. # handle fixup of the email recipient list.
  534. fix_to_email() {
  535. to_email=
  536. while [ -n "${1}" ]; do
  537. [ -n "${to_email}" ] && to_email="${to_email}, "
  538. to_email="${to_email}${1}"
  539. shift 1
  540. done
  541. }
  542. # ${to_email} without quotes here
  543. fix_to_email ${to_email}
  544. # -----------------------------------------------------------------------------
  545. # handle output if we're running in unit test mode
  546. if [ ${unittest} ]; then
  547. for method_name in ${method_names}; do
  548. to_var="to_${method_name}"
  549. echo "results: ${method_name}: ${!to_var}"
  550. done
  551. exit 0
  552. fi
  553. # -----------------------------------------------------------------------------
  554. # check that we have at least a method enabled
  555. proceed=0
  556. for method in "${SEND_EMAIL}" \
  557. "${SEND_PUSHOVER}" \
  558. "${SEND_TELEGRAM}" \
  559. "${SEND_SLACK}" \
  560. "${SEND_ROCKETCHAT}" \
  561. "${SEND_ALERTA}" \
  562. "${SEND_FLOCK}" \
  563. "${SEND_DISCORD}" \
  564. "${SEND_TWILIO}" \
  565. "${SEND_HIPCHAT}" \
  566. "${SEND_MESSAGEBIRD}" \
  567. "${SEND_KAVENEGAR}" \
  568. "${SEND_PUSHBULLET}" \
  569. "${SEND_KAFKA}" \
  570. "${SEND_PD}" \
  571. "${SEND_FLEEP}" \
  572. "${SEND_PROWL}" \
  573. "${SEND_CUSTOM}" \
  574. "${SEND_IRC}" \
  575. "${SEND_HANGOUTS}" \
  576. "${SEND_AWSSNS}" \
  577. "${SEND_SYSLOG}" \
  578. "${SEND_SMS}" \
  579. "${SEND_MSTEAM}" \
  580. "${SEND_DYNATRACE}"; do
  581. if [ "${method}" == "YES" ]; then
  582. proceed=1
  583. break
  584. fi
  585. done
  586. if [ "$proceed" -eq 0 ]; then
  587. fatal "All notification methods are disabled. Not sending notification for host '${host}', chart '${chart}' to '${roles}' for '${name}' = '${value}' for status '${status}'."
  588. fi
  589. # -----------------------------------------------------------------------------
  590. # get the date the alarm happened
  591. date=$(date --date=@${when} "${date_format}" 2>/dev/null)
  592. [ -z "${date}" ] && date=$(date "${date_format}" 2>/dev/null)
  593. [ -z "${date}" ] && date=$(date --date=@${when} 2>/dev/null)
  594. [ -z "${date}" ] && date=$(date 2>/dev/null)
  595. # ----------------------------------------------------------------------------
  596. # prepare some extra headers if we've been asked to thread e-mails
  597. if [ "${SEND_EMAIL}" == "YES" ] && [ "${EMAIL_THREADING}" != "NO" ]; then
  598. email_thread_headers="In-Reply-To: <${chart}-${name}@${host}>\\r\\nReferences: <${chart}-${name}@${host}>"
  599. else
  600. email_thread_headers=
  601. fi
  602. # -----------------------------------------------------------------------------
  603. # function to URL encode a string
  604. urlencode() {
  605. local string="${1}" strlen encoded pos c o
  606. strlen=${#string}
  607. for ((pos = 0; pos < strlen; pos++)); do
  608. c=${string:pos:1}
  609. case "${c}" in
  610. [-_.~a-zA-Z0-9])
  611. o="${c}"
  612. ;;
  613. *)
  614. printf -v o '%%%02x' "'${c}"
  615. ;;
  616. esac
  617. encoded+="${o}"
  618. done
  619. REPLY="${encoded}"
  620. echo "${REPLY}"
  621. }
  622. # -----------------------------------------------------------------------------
  623. # function to convert a duration in seconds, to a human readable duration
  624. # using DAYS, MINUTES, SECONDS
  625. duration4human() {
  626. local s="${1}" d=0 h=0 m=0 ds="day" hs="hour" ms="minute" ss="second" ret
  627. d=$((s / 86400))
  628. s=$((s - (d * 86400)))
  629. h=$((s / 3600))
  630. s=$((s - (h * 3600)))
  631. m=$((s / 60))
  632. s=$((s - (m * 60)))
  633. if [ ${d} -gt 0 ]; then
  634. [ ${m} -ge 30 ] && h=$((h + 1))
  635. [ ${d} -gt 1 ] && ds="days"
  636. [ ${h} -gt 1 ] && hs="hours"
  637. if [ ${h} -gt 0 ]; then
  638. ret="${d} ${ds} and ${h} ${hs}"
  639. else
  640. ret="${d} ${ds}"
  641. fi
  642. elif [ ${h} -gt 0 ]; then
  643. [ ${s} -ge 30 ] && m=$((m + 1))
  644. [ ${h} -gt 1 ] && hs="hours"
  645. [ ${m} -gt 1 ] && ms="minutes"
  646. if [ ${m} -gt 0 ]; then
  647. ret="${h} ${hs} and ${m} ${ms}"
  648. else
  649. ret="${h} ${hs}"
  650. fi
  651. elif [ ${m} -gt 0 ]; then
  652. [ ${m} -gt 1 ] && ms="minutes"
  653. [ ${s} -gt 1 ] && ss="seconds"
  654. if [ ${s} -gt 0 ]; then
  655. ret="${m} ${ms} and ${s} ${ss}"
  656. else
  657. ret="${m} ${ms}"
  658. fi
  659. else
  660. [ ${s} -gt 1 ] && ss="seconds"
  661. ret="${s} ${ss}"
  662. fi
  663. REPLY="${ret}"
  664. echo "${REPLY}"
  665. }
  666. # -----------------------------------------------------------------------------
  667. # email sender
  668. send_email() {
  669. local ret opts=() sender_email="${EMAIL_SENDER}" sender_name=
  670. if [ "${SEND_EMAIL}" = "YES" ]; then
  671. if [ -n "${EMAIL_SENDER}" ]; then
  672. if [[ ${EMAIL_SENDER} =~ ^\".*\"\ \<.*\>$ ]]; then
  673. # the name includes double quotes
  674. sender_email="$(echo "${EMAIL_SENDER}" | cut -d '<' -f 2 | cut -d '>' -f 1)"
  675. sender_name="$(echo "${EMAIL_SENDER}" | cut -d '"' -f 2)"
  676. elif [[ ${EMAIL_SENDER} =~ ^\'.*\'\ \<.*\>$ ]]; then
  677. # the name includes single quotes
  678. sender_email="$(echo "${EMAIL_SENDER}" | cut -d '<' -f 2 | cut -d '>' -f 1)"
  679. sender_name="$(echo "${EMAIL_SENDER}" | cut -d "'" -f 2)"
  680. elif [[ ${EMAIL_SENDER} =~ ^.*\ \<.*\>$ ]]; then
  681. # the name does not have any quotes
  682. sender_email="$(echo "${EMAIL_SENDER}" | cut -d '<' -f 2 | cut -d '>' -f 1)"
  683. sender_name="$(echo "${EMAIL_SENDER}" | cut -d '<' -f 1)"
  684. fi
  685. fi
  686. [ -n "${sender_email}" ] && opts+=(-f "${sender_email}")
  687. [ -n "${sender_name}" ] && opts+=(-F "${sender_name}")
  688. if [ "${debug}" = "1" ]; then
  689. echo >&2 "--- BEGIN sendmail command ---"
  690. printf >&2 "%q " "${sendmail}" -t "${opts[@]}"
  691. echo >&2
  692. echo >&2 "--- END sendmail command ---"
  693. fi
  694. "${sendmail}" -t "${opts[@]}"
  695. ret=$?
  696. if [ ${ret} -eq 0 ]; then
  697. info "sent email notification for: ${host} ${chart}.${name} is ${status} to '${to_email}'"
  698. return 0
  699. else
  700. error "failed to send email notification for: ${host} ${chart}.${name} is ${status} to '${to_email}' with error code ${ret}."
  701. return 1
  702. fi
  703. fi
  704. return 1
  705. }
  706. # -----------------------------------------------------------------------------
  707. # pushover sender
  708. send_pushover() {
  709. local apptoken="${1}" usertokens="${2}" when="${3}" url="${4}" status="${5}" title="${6}" message="${7}" httpcode sent=0 user priority
  710. if [ "${SEND_PUSHOVER}" = "YES" ] && [ -n "${apptoken}" ] && [ -n "${usertokens}" ] && [ -n "${title}" ] && [ -n "${message}" ]; then
  711. # https://pushover.net/api
  712. priority=-2
  713. case "${status}" in
  714. CLEAR) priority=-1 ;; # low priority: no sound or vibration
  715. WARNING) priority=0 ;; # normal priority: respect quiet hours
  716. CRITICAL) priority=1 ;; # high priority: bypass quiet hours
  717. *) priority=-2 ;; # lowest priority: no notification at all
  718. esac
  719. for user in ${usertokens}; do
  720. httpcode=$(docurl \
  721. --form-string "token=${apptoken}" \
  722. --form-string "user=${user}" \
  723. --form-string "html=1" \
  724. --form-string "title=${title}" \
  725. --form-string "message=${message}" \
  726. --form-string "timestamp=${when}" \
  727. --form-string "url=${url}" \
  728. --form-string "url_title=Open netdata dashboard to view the alarm" \
  729. --form-string "priority=${priority}" \
  730. https://api.pushover.net/1/messages.json)
  731. if [ "${httpcode}" = "200" ]; then
  732. info "sent pushover notification for: ${host} ${chart}.${name} is ${status} to '${user}'"
  733. sent=$((sent + 1))
  734. else
  735. error "failed to send pushover notification for: ${host} ${chart}.${name} is ${status} to '${user}' with HTTP error code ${httpcode}."
  736. fi
  737. done
  738. [ ${sent} -gt 0 ] && return 0
  739. fi
  740. return 1
  741. }
  742. # -----------------------------------------------------------------------------
  743. # pushbullet sender
  744. send_pushbullet() {
  745. local userapikey="${1}" source_device="${2}" recipients="${3}" url="${4}" title="${5}" message="${6}" httpcode sent=0 user
  746. if [ "${SEND_PUSHBULLET}" = "YES" ] && [ -n "${userapikey}" ] && [ -n "${recipients}" ] && [ -n "${message}" ] && [ -n "${title}" ]; then
  747. #https://docs.pushbullet.com/#create-push
  748. for user in ${recipients}; do
  749. httpcode=$(docurl \
  750. --header 'Access-Token: '${userapikey}'' \
  751. --header 'Content-Type: application/json' \
  752. --data-binary @<(
  753. cat <<EOF
  754. {"title": "${title}",
  755. "type": "link",
  756. "email": "${user}",
  757. "body": "$(echo -n ${message})",
  758. "url": "${url}",
  759. "source_device_iden": "${source_device}"}
  760. EOF
  761. ) "https://api.pushbullet.com/v2/pushes" -X POST)
  762. if [ "${httpcode}" = "200" ]; then
  763. info "sent pushbullet notification for: ${host} ${chart}.${name} is ${status} to '${user}'"
  764. sent=$((sent + 1))
  765. else
  766. error "failed to send pushbullet notification for: ${host} ${chart}.${name} is ${status} to '${user}' with HTTP error code ${httpcode}."
  767. fi
  768. done
  769. [ ${sent} -gt 0 ] && return 0
  770. fi
  771. return 1
  772. }
  773. # -----------------------------------------------------------------------------
  774. # kafka sender
  775. send_kafka() {
  776. local httpcode sent=0
  777. if [ "${SEND_KAFKA}" = "YES" ]; then
  778. httpcode=$(docurl -X POST \
  779. --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}\"}" \
  780. "${KAFKA_URL}")
  781. if [ "${httpcode}" = "204" ]; then
  782. info "sent kafka data for: ${host} ${chart}.${name} is ${status} and ip '${KAFKA_SENDER_IP}'"
  783. sent=$((sent + 1))
  784. else
  785. error "failed to send kafka data for: ${host} ${chart}.${name} is ${status} and ip '${KAFKA_SENDER_IP}' with HTTP error code ${httpcode}."
  786. fi
  787. [ ${sent} -gt 0 ] && return 0
  788. fi
  789. return 1
  790. }
  791. # -----------------------------------------------------------------------------
  792. # pagerduty.com sender
  793. send_pd() {
  794. local recipients="${1}" sent=0
  795. unset t
  796. case ${status} in
  797. CLEAR) t='resolve' ;;
  798. WARNING) t='trigger' ;;
  799. CRITICAL) t='trigger' ;;
  800. esac
  801. if [ ${SEND_PD} = "YES" ] && [ -n "${t}" ]; then
  802. for PD_SERVICE_KEY in ${recipients}; do
  803. d="${status} ${name} = ${value_string} - ${host}, ${family}"
  804. payload="$(
  805. cat <<EOF
  806. {
  807. "service_key": "${PD_SERVICE_KEY}",
  808. "event_type": "${t}",
  809. "incident_key" : "${alarm_id}",
  810. "description": "${d}",
  811. "details": {
  812. "value_w_units": "${value_string}",
  813. "when": "${when}",
  814. "duration" : "${duration}",
  815. "roles": "${roles}",
  816. "alarm_id" : "${alarm_id}",
  817. "name" : "${name}",
  818. "chart" : "${chart}",
  819. "family" : "${family}",
  820. "status" : "${status}",
  821. "old_status" : "${old_status}",
  822. "value" : "${value}",
  823. "old_value" : "${old_value}",
  824. "src" : "${src}",
  825. "non_clear_duration" : "${non_clear_duration}",
  826. "units" : "${units}",
  827. "info" : "${info}"
  828. }
  829. }
  830. EOF
  831. )"
  832. httpcode=$(docurl -X POST --data "${payload}" "https://events.pagerduty.com/generic/2010-04-15/create_event.json")
  833. if [ "${httpcode}" = "200" ]; then
  834. info "sent pagerduty notification for: ${host} ${chart}.${name} is ${status}'"
  835. sent=$((sent + 1))
  836. else
  837. error "failed to send pagerduty notification for: ${host} ${chart}.${name} is ${status}, with HTTP error code ${httpcode}."
  838. fi
  839. done
  840. [ ${sent} -gt 0 ] && return 0
  841. fi
  842. return 1
  843. }
  844. # -----------------------------------------------------------------------------
  845. # twilio sender
  846. send_twilio() {
  847. local accountsid="${1}" accounttoken="${2}" twilionumber="${3}" recipients="${4}" title="${5}" message="${6}" httpcode sent=0 user
  848. if [ "${SEND_TWILIO}" = "YES" ] && [ -n "${accountsid}" ] && [ -n "${accounttoken}" ] && [ -n "${twilionumber}" ] && [ -n "${recipients}" ] && [ -n "${message}" ] && [ -n "${title}" ]; then
  849. #https://www.twilio.com/packages/labs/code/bash/twilio-sms
  850. for user in ${recipients}; do
  851. httpcode=$(docurl -X POST \
  852. --data-urlencode "From=${twilionumber}" \
  853. --data-urlencode "To=${user}" \
  854. --data-urlencode "Body=${title} ${message}" \
  855. -u "${accountsid}:${accounttoken}" \
  856. "https://api.twilio.com/2010-04-01/Accounts/${accountsid}/Messages.json")
  857. if [ "${httpcode}" = "201" ]; then
  858. info "sent Twilio SMS for: ${host} ${chart}.${name} is ${status} to '${user}'"
  859. sent=$((sent + 1))
  860. else
  861. error "failed to send Twilio SMS for: ${host} ${chart}.${name} is ${status} to '${user}' with HTTP error code ${httpcode}."
  862. fi
  863. done
  864. [ ${sent} -gt 0 ] && return 0
  865. fi
  866. return 1
  867. }
  868. # -----------------------------------------------------------------------------
  869. # hipchat sender
  870. send_hipchat() {
  871. local authtoken="${1}" recipients="${2}" message="${3}" httpcode sent=0 room color msg_format notify
  872. # remove <small></small> from the message
  873. message="${message//<small>/}"
  874. message="${message//<\/small>/}"
  875. if [ "${SEND_HIPCHAT}" = "YES" ] && [ -n "${HIPCHAT_SERVER}" ] && [ -n "${authtoken}" ] && [ -n "${recipients}" ] && [ -n "${message}" ]; then
  876. # Valid values: html, text.
  877. # Defaults to 'html'.
  878. msg_format="html"
  879. # Background color for message. Valid values: yellow, green, red, purple, gray, random. Defaults to 'yellow'.
  880. case "${status}" in
  881. WARNING) color="yellow" ;;
  882. CRITICAL) color="red" ;;
  883. CLEAR) color="green" ;;
  884. *) color="gray" ;;
  885. esac
  886. # Whether this message should trigger a user notification (change the tab color, play a sound, notify mobile phones, etc).
  887. # Each recipient's notification preferences are taken into account.
  888. # Defaults to false.
  889. notify="true"
  890. for room in ${recipients}; do
  891. httpcode=$(docurl -X POST \
  892. -H "Content-type: application/json" \
  893. -H "Authorization: Bearer ${authtoken}" \
  894. -d "{\"color\": \"${color}\", \"from\": \"${host}\", \"message_format\": \"${msg_format}\", \"message\": \"${message}\", \"notify\": \"${notify}\"}" \
  895. "https://${HIPCHAT_SERVER}/v2/room/${room}/notification")
  896. if [ "${httpcode}" = "204" ]; then
  897. info "sent HipChat notification for: ${host} ${chart}.${name} is ${status} to '${room}'"
  898. sent=$((sent + 1))
  899. else
  900. error "failed to send HipChat notification for: ${host} ${chart}.${name} is ${status} to '${room}' with HTTP error code ${httpcode}."
  901. fi
  902. done
  903. [ ${sent} -gt 0 ] && return 0
  904. fi
  905. return 1
  906. }
  907. # -----------------------------------------------------------------------------
  908. # messagebird sender
  909. send_messagebird() {
  910. local accesskey="${1}" messagebirdnumber="${2}" recipients="${3}" title="${4}" message="${5}" httpcode sent=0 user
  911. if [ "${SEND_MESSAGEBIRD}" = "YES" ] && [ -n "${accesskey}" ] && [ -n "${messagebirdnumber}" ] && [ -n "${recipients}" ] && [ -n "${message}" ] && [ -n "${title}" ]; then
  912. #https://developers.messagebird.com/docs/messaging
  913. for user in ${recipients}; do
  914. httpcode=$(docurl -X POST \
  915. --data-urlencode "originator=${messagebirdnumber}" \
  916. --data-urlencode "recipients=${user}" \
  917. --data-urlencode "body=${title} ${message}" \
  918. --data-urlencode "datacoding=auto" \
  919. -H "Authorization: AccessKey ${accesskey}" \
  920. "https://rest.messagebird.com/messages")
  921. if [ "${httpcode}" = "201" ]; then
  922. info "sent Messagebird SMS for: ${host} ${chart}.${name} is ${status} to '${user}'"
  923. sent=$((sent + 1))
  924. else
  925. error "failed to send Messagebird SMS for: ${host} ${chart}.${name} is ${status} to '${user}' with HTTP error code ${httpcode}."
  926. fi
  927. done
  928. [ ${sent} -gt 0 ] && return 0
  929. fi
  930. return 1
  931. }
  932. # -----------------------------------------------------------------------------
  933. # kavenegar sender
  934. send_kavenegar() {
  935. local API_KEY="${1}" kavenegarsender="${2}" recipients="${3}" title="${4}" message="${5}" httpcode sent=0 user
  936. if [ "${SEND_KAVENEGAR}" = "YES" ] && [ -n "${API_KEY}" ] && [ -n "${kavenegarsender}" ] && [ -n "${recipients}" ] && [ -n "${message}" ] && [ -n "${title}" ]; then
  937. # http://api.kavenegar.com/v1/{API-KEY}/sms/send.json
  938. for user in ${recipients}; do
  939. httpcode=$(docurl -X POST http://api.kavenegar.com/v1/${API_KEY}/sms/send.json \
  940. --data-urlencode "sender=${kavenegarsender}" \
  941. --data-urlencode "receptor=${user}" \
  942. --data-urlencode "message=${title} ${message}")
  943. if [ "${httpcode}" = "200" ]; then
  944. info "sent Kavenegar SMS for: ${host} ${chart}.${name} is ${status} to '${user}'"
  945. sent=$((sent + 1))
  946. else
  947. error "failed to send Kavenegar SMS for: ${host} ${chart}.${name} is ${status} to '${user}' with HTTP error code ${httpcode}."
  948. fi
  949. done
  950. [ ${sent} -gt 0 ] && return 0
  951. fi
  952. return 1
  953. }
  954. # -----------------------------------------------------------------------------
  955. # telegram sender
  956. send_telegram() {
  957. local bottoken="${1}" chatids="${2}" message="${3}" httpcode sent=0 chatid emoji disableNotification=""
  958. if [ "${status}" = "CLEAR" ]; then disableNotification="--data-urlencode disable_notification=true"; fi
  959. case "${status}" in
  960. WARNING) emoji="⚠️" ;;
  961. CRITICAL) emoji="🔴" ;;
  962. CLEAR) emoji="✅" ;;
  963. *) emoji="⚪️" ;;
  964. esac
  965. if [ "${SEND_TELEGRAM}" = "YES" ] && [ -n "${bottoken}" ] && [ -n "${chatids}" ] && [ -n "${message}" ]; then
  966. for chatid in ${chatids}; do
  967. # https://core.telegram.org/bots/api#sendmessage
  968. httpcode=$(docurl ${disableNotification} \
  969. --data-urlencode "parse_mode=HTML" \
  970. --data-urlencode "disable_web_page_preview=true" \
  971. --data-urlencode "text=${emoji} ${message}" \
  972. "https://api.telegram.org/bot${bottoken}/sendMessage?chat_id=${chatid}")
  973. if [ "${httpcode}" = "200" ]; then
  974. info "sent telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}'"
  975. sent=$((sent + 1))
  976. elif [ "${httpcode}" = "401" ]; then
  977. error "failed to send telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}': Wrong bot token."
  978. else
  979. error "failed to send telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}' with HTTP error code ${httpcode}."
  980. fi
  981. done
  982. [ ${sent} -gt 0 ] && return 0
  983. fi
  984. return 1
  985. }
  986. # -----------------------------------------------------------------------------
  987. # Microsoft Team sender
  988. send_msteam() {
  989. local webhook="${1}" channels="${2}" httpcode sent=0 channel color payload
  990. [ "${SEND_MSTEAM}" != "YES" ] && return 1
  991. case "${status}" in
  992. WARNING) icon="${MSTEAM_ICON_WARNING}" && color="${MSTEAM_COLOR_WARNING}" ;;
  993. CRITICAL) icon="${MSTEAM_ICON_CRITICAL}" && color="${MSTEAM_COLOR_CRITICAL}" ;;
  994. CLEAR) icon="${MSTEAM_ICON_CLEAR}" && color="${MSTEAM_COLOR_CLEAR}" ;;
  995. *) icon="${MSTEAM_ICON_DEFAULT}" && color="${MSTEAM_COLOR_DEFAULT}" ;;
  996. esac
  997. for channel in ${channels}; do
  998. ## More details are available here regarding the payload syntax options : https://docs.microsoft.com/en-us/outlook/actionable-messages/message-card-reference
  999. ## Online designer : https://acdesignerbeta.azurewebsites.net/
  1000. payload="$(
  1001. cat <<EOF
  1002. {
  1003. "@context": "http://schema.org/extensions",
  1004. "@type": "MessageCard",
  1005. "themeColor": "${color}",
  1006. "title": "$icon Alert ${status} from netdata for ${host}",
  1007. "text": "${host} ${status_message}, ${chart} (_${family}_), *${alarm}*",
  1008. "potentialAction": [
  1009. {
  1010. "@type": "OpenUri",
  1011. "name": "Netdata",
  1012. "targets": [
  1013. { "os": "default", "uri": "${goto_url}" }
  1014. ]
  1015. }
  1016. ]
  1017. }
  1018. EOF
  1019. )"
  1020. # Replacing in the webhook CHANNEL string by the MS Teams channel name from conf file.
  1021. webhook="${webhook//CHANNEL/${channel}}"
  1022. httpcode=$(docurl -H "Content-Type: application/json" -d "${payload}" "${webhook}")
  1023. if [ "${httpcode}" = "200" ]; then
  1024. info "sent Microsoft team notification for: ${host} ${chart}.${name} is ${status} to '${webhook}'"
  1025. sent=$((sent + 1))
  1026. else
  1027. error "failed to send Microsoft team notification for: ${host} ${chart}.${name} is ${status} to '${webhook}', with HTTP error code ${httpcode}."
  1028. fi
  1029. done
  1030. [ ${sent} -gt 0 ] && return 0
  1031. return 1
  1032. }
  1033. # slack sender
  1034. send_slack() {
  1035. local webhook="${1}" channels="${2}" httpcode sent=0 channel color payload
  1036. [ "${SEND_SLACK}" != "YES" ] && return 1
  1037. case "${status}" in
  1038. WARNING) color="warning" ;;
  1039. CRITICAL) color="danger" ;;
  1040. CLEAR) color="good" ;;
  1041. *) color="#777777" ;;
  1042. esac
  1043. for channel in ${channels}; do
  1044. # Default entry in the recipient is without a hash in front (backwards-compatible). Accept specification of channel or user.
  1045. if [ "${channel::1}" != "#" ] && [ "${channel::1}" != "@" ]; then channel="#$channel"; fi
  1046. # If channel is equal to "#" then do not send the channel attribute at all. Slack also defines channels and users in webhooks.
  1047. if [ "${channel}" = "#" ]; then
  1048. ch=""
  1049. chstr="without specifying a channel"
  1050. else
  1051. ch="\"channel\": \"${channel}\","
  1052. chstr="to '${channel}'"
  1053. fi
  1054. payload="$(
  1055. cat <<EOF
  1056. {
  1057. $ch
  1058. "username": "netdata on ${host}",
  1059. "icon_url": "${images_base_url}/images/banner-icon-144x144.png",
  1060. "text": "${host} ${status_message}, \`${chart}\` (_${family}_), *${alarm}*",
  1061. "attachments": [
  1062. {
  1063. "fallback": "${alarm} - ${chart} (${family}) - ${info}",
  1064. "color": "${color}",
  1065. "title": "${alarm}",
  1066. "title_link": "${goto_url}",
  1067. "text": "${info}",
  1068. "fields": [
  1069. {
  1070. "title": "${chart}",
  1071. "short": true
  1072. },
  1073. {
  1074. "title": "${family}",
  1075. "short": true
  1076. }
  1077. ],
  1078. "thumb_url": "${image}",
  1079. "footer": "by ${host}",
  1080. "ts": ${when}
  1081. }
  1082. ]
  1083. }
  1084. EOF
  1085. )"
  1086. httpcode=$(docurl -X POST --data-urlencode "payload=${payload}" "${webhook}")
  1087. if [ "${httpcode}" = "200" ]; then
  1088. info "sent slack notification for: ${host} ${chart}.${name} is ${status} ${chstr}"
  1089. sent=$((sent + 1))
  1090. else
  1091. error "failed to send slack notification for: ${host} ${chart}.${name} is ${status} ${chstr}, with HTTP error code ${httpcode}."
  1092. fi
  1093. done
  1094. [ ${sent} -gt 0 ] && return 0
  1095. return 1
  1096. }
  1097. # -----------------------------------------------------------------------------
  1098. # rocketchat sender
  1099. send_rocketchat() {
  1100. local webhook="${1}" channels="${2}" httpcode sent=0 channel color payload
  1101. [ "${SEND_ROCKETCHAT}" != "YES" ] && return 1
  1102. case "${status}" in
  1103. WARNING) color="warning" ;;
  1104. CRITICAL) color="danger" ;;
  1105. CLEAR) color="good" ;;
  1106. *) color="#777777" ;;
  1107. esac
  1108. for channel in ${channels}; do
  1109. payload="$(
  1110. cat <<EOF
  1111. {
  1112. "channel": "#${channel}",
  1113. "alias": "netdata on ${host}",
  1114. "avatar": "${images_base_url}/images/banner-icon-144x144.png",
  1115. "text": "${host} ${status_message}, \`${chart}\` (_${family}_), *${alarm}*",
  1116. "attachments": [
  1117. {
  1118. "color": "${color}",
  1119. "title": "${alarm}",
  1120. "title_link": "${goto_url}",
  1121. "text": "${info}",
  1122. "fields": [
  1123. {
  1124. "title": "${chart}",
  1125. "short": true,
  1126. "value": "chart"
  1127. },
  1128. {
  1129. "title": "${family}",
  1130. "short": true,
  1131. "value": "family"
  1132. }
  1133. ],
  1134. "thumb_url": "${image}",
  1135. "ts": "${when}"
  1136. }
  1137. ]
  1138. }
  1139. EOF
  1140. )"
  1141. httpcode=$(docurl -X POST --data-urlencode "payload=${payload}" "${webhook}")
  1142. if [ "${httpcode}" = "200" ]; then
  1143. info "sent rocketchat notification for: ${host} ${chart}.${name} is ${status} to '${channel}'"
  1144. sent=$((sent + 1))
  1145. else
  1146. error "failed to send rocketchat notification for: ${host} ${chart}.${name} is ${status} to '${channel}', with HTTP error code ${httpcode}."
  1147. fi
  1148. done
  1149. [ ${sent} -gt 0 ] && return 0
  1150. return 1
  1151. }
  1152. # -----------------------------------------------------------------------------
  1153. # alerta sender
  1154. send_alerta() {
  1155. local webhook="${1}" channels="${2}" httpcode sent=0 channel severity resource event payload auth
  1156. [ "${SEND_ALERTA}" != "YES" ] && return 1
  1157. case "${status}" in
  1158. CRITICAL) severity="critical" ;;
  1159. WARNING) severity="warning" ;;
  1160. CLEAR) severity="cleared" ;;
  1161. *) severity="indeterminate" ;;
  1162. esac
  1163. if [[ ${chart} == httpcheck* ]]; then
  1164. resource=$chart
  1165. event=$name
  1166. else
  1167. resource="${host}:${family}"
  1168. event="${chart}.${name}"
  1169. fi
  1170. for channel in ${channels}; do
  1171. payload="$(
  1172. cat <<EOF
  1173. {
  1174. "resource": "${resource}",
  1175. "event": "${event}",
  1176. "environment": "${channel}",
  1177. "severity": "${severity}",
  1178. "service": ["Netdata"],
  1179. "group": "Performance",
  1180. "value": "${value_string}",
  1181. "text": "${info}",
  1182. "tags": ["alarm_id:${alarm_id}"],
  1183. "attributes": {
  1184. "roles": "${roles}",
  1185. "name": "${name}",
  1186. "chart": "${chart}",
  1187. "family": "${family}",
  1188. "source": "${src}",
  1189. "moreInfo": "<a href=\"${goto_url}\">View Netdata</a>"
  1190. },
  1191. "origin": "netdata/${host}",
  1192. "type": "netdataAlarm",
  1193. "rawData": "${BASH_ARGV[@]}"
  1194. }
  1195. EOF
  1196. )"
  1197. if [ -n "${ALERTA_API_KEY}" ]; then
  1198. auth="Key ${ALERTA_API_KEY}"
  1199. fi
  1200. httpcode=$(docurl -X POST "${webhook}/alert" -H "Content-Type: application/json" -H "Authorization: $auth" --data "${payload}")
  1201. if [ "${httpcode}" = "200" ] || [ "${httpcode}" = "201" ]; then
  1202. info "sent alerta notification for: ${host} ${chart}.${name} is ${status} to '${channel}'"
  1203. sent=$((sent + 1))
  1204. elif [ "${httpcode}" = "202" ]; then
  1205. info "suppressed alerta notification for: ${host} ${chart}.${name} is ${status} to '${channel}'"
  1206. else
  1207. error "failed to send alerta notification for: ${host} ${chart}.${name} is ${status} to '${channel}', with HTTP error code ${httpcode}."
  1208. fi
  1209. done
  1210. [ ${sent} -gt 0 ] && return 0
  1211. return 1
  1212. }
  1213. # -----------------------------------------------------------------------------
  1214. # flock sender
  1215. send_flock() {
  1216. local webhook="${1}" channels="${2}" httpcode sent=0 channel color payload
  1217. [ "${SEND_FLOCK}" != "YES" ] && return 1
  1218. case "${status}" in
  1219. WARNING) color="warning" ;;
  1220. CRITICAL) color="danger" ;;
  1221. CLEAR) color="good" ;;
  1222. *) color="#777777" ;;
  1223. esac
  1224. for channel in ${channels}; do
  1225. httpcode=$(docurl -X POST "${webhook}" -H "Content-Type: application/json" -d "{
  1226. \"sendAs\": {
  1227. \"name\" : \"netdata on ${host}\",
  1228. \"profileImage\" : \"${images_base_url}/images/banner-icon-144x144.png\"
  1229. },
  1230. \"text\": \"${host} *${status_message}*\",
  1231. \"timestamp\": \"${when}\",
  1232. \"attachments\": [
  1233. {
  1234. \"description\": \"${chart} (${family}) - ${info}\",
  1235. \"color\": \"${color}\",
  1236. \"title\": \"${alarm}\",
  1237. \"url\": \"${goto_url}\",
  1238. \"text\": \"${info}\",
  1239. \"views\": {
  1240. \"image\": {
  1241. \"original\": { \"src\": \"${image}\", \"width\": 400, \"height\": 400 },
  1242. \"thumbnail\": { \"src\": \"${image}\", \"width\": 50, \"height\": 50 },
  1243. \"filename\": \"${image}\"
  1244. }
  1245. }
  1246. }
  1247. ]
  1248. }")
  1249. if [ "${httpcode}" = "200" ]; then
  1250. info "sent flock notification for: ${host} ${chart}.${name} is ${status} to '${channel}'"
  1251. sent=$((sent + 1))
  1252. else
  1253. error "failed to send flock notification for: ${host} ${chart}.${name} is ${status} to '${channel}', with HTTP error code ${httpcode}."
  1254. fi
  1255. done
  1256. [ ${sent} -gt 0 ] && return 0
  1257. return 1
  1258. }
  1259. # -----------------------------------------------------------------------------
  1260. # discord sender
  1261. send_discord() {
  1262. local webhook="${1}/slack" channels="${2}" httpcode sent=0 channel color payload username
  1263. [ "${SEND_DISCORD}" != "YES" ] && return 1
  1264. case "${status}" in
  1265. WARNING) color="warning" ;;
  1266. CRITICAL) color="danger" ;;
  1267. CLEAR) color="good" ;;
  1268. *) color="#777777" ;;
  1269. esac
  1270. for channel in ${channels}; do
  1271. username="netdata on ${host}"
  1272. [ ${#username} -gt 32 ] && username="${username:0:29}..."
  1273. payload="$(
  1274. cat <<EOF
  1275. {
  1276. "channel": "#${channel}",
  1277. "username": "${username}",
  1278. "text": "${host} ${status_message}, \`${chart}\` (_${family}_), *${alarm}*",
  1279. "icon_url": "${images_base_url}/images/banner-icon-144x144.png",
  1280. "attachments": [
  1281. {
  1282. "color": "${color}",
  1283. "title": "${alarm}",
  1284. "title_link": "${goto_url}",
  1285. "text": "${info}",
  1286. "fields": [
  1287. {
  1288. "title": "${chart}",
  1289. "value": "${family}"
  1290. }
  1291. ],
  1292. "thumb_url": "${image}",
  1293. "footer_icon": "${images_base_url}/images/banner-icon-144x144.png",
  1294. "footer": "${host}",
  1295. "ts": ${when}
  1296. }
  1297. ]
  1298. }
  1299. EOF
  1300. )"
  1301. httpcode=$(docurl -X POST --data-urlencode "payload=${payload}" "${webhook}")
  1302. if [ "${httpcode}" = "200" ]; then
  1303. info "sent discord notification for: ${host} ${chart}.${name} is ${status} to '${channel}'"
  1304. sent=$((sent + 1))
  1305. else
  1306. error "failed to send discord notification for: ${host} ${chart}.${name} is ${status} to '${channel}', with HTTP error code ${httpcode}."
  1307. fi
  1308. done
  1309. [ ${sent} -gt 0 ] && return 0
  1310. return 1
  1311. }
  1312. # -----------------------------------------------------------------------------
  1313. # fleep sender
  1314. send_fleep() {
  1315. local httpcode sent=0 webhooks="${1}" data message
  1316. if [ "${SEND_FLEEP}" = "YES" ]; then
  1317. message="${host} ${status_message}, \`${chart}\` (${family}), *${alarm}*\\n${info}"
  1318. for hook in ${webhooks}; do
  1319. data="{ "
  1320. data="${data} 'message': '${message}', "
  1321. data="${data} 'user': '${FLEEP_SENDER}' "
  1322. data="${data} }"
  1323. httpcode=$(docurl -X POST --data "${data}" "https://fleep.io/hook/${hook}")
  1324. if [ "${httpcode}" = "200" ]; then
  1325. info "sent fleep data for: ${host} ${chart}.${name} is ${status} and user '${FLEEP_SENDER}'"
  1326. sent=$((sent + 1))
  1327. else
  1328. error "failed to send fleep data for: ${host} ${chart}.${name} is ${status} and user '${FLEEP_SENDER}' with HTTP error code ${httpcode}."
  1329. fi
  1330. done
  1331. [ ${sent} -gt 0 ] && return 0
  1332. fi
  1333. return 1
  1334. }
  1335. # -----------------------------------------------------------------------------
  1336. # Prowl sender
  1337. send_prowl() {
  1338. local httpcode sent=0 data message keys prio=0 alarm_url event
  1339. if [ "${SEND_PROWL}" = "YES" ]; then
  1340. message="$(urlencode "${host} ${status_message}, \`${chart}\` (${family}), *${alarm}*\\n${info}")"
  1341. message="description=${message}"
  1342. keys="$(urlencode "$(echo "${1}" | tr ' ' ,)")"
  1343. keys="apikey=${keys}"
  1344. app="application=Netdata"
  1345. case "${status}" in
  1346. CRITICAL)
  1347. prio=2
  1348. ;;
  1349. WARNING)
  1350. prio=1
  1351. ;;
  1352. esac
  1353. prio="priority=${prio}"
  1354. alarm_url="$(urlencode ${goto_url})"
  1355. alarm_url="url=${alarm_url}"
  1356. event="$(urlencode "${host} ${status_message}")"
  1357. event="event=${event}"
  1358. data="${keys}&${prio}&${alarm_url}&${app}&${event}&${message}"
  1359. httpcode=$(docurl -X POST --data "${data}" "https://api.prowlapp.com/publicapi/add")
  1360. if [ "${httpcode}" = "200" ]; then
  1361. info "sent prowl data for: ${host} ${chart}.${name} is ${status}"
  1362. sent=1
  1363. else
  1364. error "failed to send prowl data for: ${host} ${chart}.${name} is ${status} with with error code ${httpcode}."
  1365. fi
  1366. [ ${sent} -gt 0 ] && return 0
  1367. fi
  1368. return 1
  1369. }
  1370. # -----------------------------------------------------------------------------
  1371. # irc sender
  1372. send_irc() {
  1373. local NICKNAME="${1}" REALNAME="${2}" CHANNELS="${3}" NETWORK="${4}" SERVERNAME="${5}" MESSAGE="${6}" sent=0 channel color send_alarm reply_codes error
  1374. if [ "${SEND_IRC}" = "YES" ] && [ -n "${NICKNAME}" ] && [ -n "${REALNAME}" ] && [ -n "${CHANNELS}" ] && [ -n "${NETWORK}" ] && [ -n "${SERVERNAME}" ]; then
  1375. case "${status}" in
  1376. WARNING) color="warning" ;;
  1377. CRITICAL) color="danger" ;;
  1378. CLEAR) color="good" ;;
  1379. *) color="#777777" ;;
  1380. esac
  1381. SNDMESSAGE="${MESSAGE//$'\n'/", "}"
  1382. for CHANNEL in ${CHANNELS}; do
  1383. error=0
  1384. send_alarm=$(echo -e "USER ${NICKNAME} guest ${REALNAME} ${SERVERNAME}\\nNICK ${NICKNAME}\\nJOIN ${CHANNEL}\\nPRIVMSG ${CHANNEL} :${SNDMESSAGE}\\nQUIT\\n" \ | nc "${NETWORK}" 6667)
  1385. reply_codes=$(echo "${send_alarm}" | cut -d ' ' -f 2 | grep -o '[0-9]*')
  1386. for code in ${reply_codes}; do
  1387. if [ "${code}" -ge 400 ] && [ "${code}" -le 599 ]; then
  1388. error=1
  1389. break
  1390. fi
  1391. done
  1392. if [ "${error}" -eq 0 ]; then
  1393. info "sent irc notification for: ${host} ${chart}.${name} is ${status} to '${CHANNEL}'"
  1394. sent=$((sent + 1))
  1395. else
  1396. error "failed to send irc notification for: ${host} ${chart}.${name} is ${status} to '${CHANNEL}', with error code ${code}."
  1397. fi
  1398. done
  1399. fi
  1400. [ ${sent} -gt 0 ] && return 0
  1401. return 1
  1402. }
  1403. # -----------------------------------------------------------------------------
  1404. # Amazon SNS sender
  1405. send_awssns() {
  1406. local targets="${1}" message='' sent=0 region=''
  1407. local default_format="${status} on ${host} at ${date}: ${chart} ${value_string}"
  1408. [ "${SEND_AWSSNS}" = "YES" ] || return 1
  1409. message=${AWSSNS_MESSAGE_FORMAT:-${default_format}}
  1410. for target in ${targets}; do
  1411. # Extract the region from the target ARN. We need to explicitly specify the region so that it matches up correctly.
  1412. region="$(echo ${target} | cut -f 4 -d ':')"
  1413. if ${aws} sns publish --region "${region}" --subject "${host} ${status_message} - ${name//_/ } - ${chart}" --message "${message}" --target-arn ${target} &>/dev/null; then
  1414. info "sent Amazon SNS notification for: ${host} ${chart}.${name} is ${status} to '${target}'"
  1415. sent=$((sent + 1))
  1416. else
  1417. error "failed to send Amazon SNS notification for: ${host} ${chart}.${name} is ${status} to '${target}'"
  1418. fi
  1419. done
  1420. [ ${sent} -gt 0 ] && return 0
  1421. return 1
  1422. }
  1423. # -----------------------------------------------------------------------------
  1424. # syslog sender
  1425. send_syslog() {
  1426. local facility=${SYSLOG_FACILITY:-"local6"} level='info' targets="${1}"
  1427. local priority='' message='' host='' port='' prefix=''
  1428. local temp1='' temp2=''
  1429. [ "${SEND_SYSLOG}" = "YES" ] || return 1
  1430. if [ "${status}" = "CRITICAL" ]; then
  1431. level='crit'
  1432. elif [ "${status}" = "WARNING" ]; then
  1433. level='warning'
  1434. fi
  1435. for target in ${targets}; do
  1436. priority="${facility}.${level}"
  1437. message=''
  1438. host=''
  1439. port=''
  1440. prefix=''
  1441. temp1=''
  1442. temp2=''
  1443. prefix=$(echo ${target} | cut -d '/' -f 2)
  1444. temp1=$(echo ${target} | cut -d '/' -f 1)
  1445. if [ ${prefix} != ${temp1} ]; then
  1446. if (echo ${temp1} | grep -q '@'); then
  1447. temp2=$(echo ${temp1} | cut -d '@' -f 1)
  1448. host=$(echo ${temp1} | cut -d '@' -f 2)
  1449. if [ ${temp2} != ${host} ]; then
  1450. priority=${temp2}
  1451. fi
  1452. port=$(echo ${host} | rev | cut -d ':' -f 1 | rev)
  1453. if (echo ${host} | grep -E -q '\[.*\]'); then
  1454. if (echo ${port} | grep -q ']'); then
  1455. port=''
  1456. else
  1457. host=$(echo ${host} | rev | cut -d ':' -f 2- | rev)
  1458. fi
  1459. else
  1460. if [ ${port} = ${host} ]; then
  1461. port=''
  1462. else
  1463. host=$(echo ${host} | cut -d ':' -f 1)
  1464. fi
  1465. fi
  1466. else
  1467. priority=${temp1}
  1468. fi
  1469. fi
  1470. message="${prefix} ${status} on ${host} at ${date}: ${chart} ${value_string}"
  1471. if [ ${host} ]; then
  1472. logger_options="${logger_options} -n ${host}"
  1473. if [ ${port} ]; then
  1474. logger_options="${logger_options} -P ${port}"
  1475. fi
  1476. fi
  1477. ${logger} -p ${priority} ${logger_options} "${message}"
  1478. done
  1479. return $?
  1480. }
  1481. # -----------------------------------------------------------------------------
  1482. # SMS sender
  1483. send_sms() {
  1484. local recipients="${1}" errcode errmessage sent=0
  1485. # Human readable SMS
  1486. local msg="${host} ${status_message}: ${chart} (${family}), ${alarm}"
  1487. # limit it to 160 characters
  1488. msg="${msg:0:160}"
  1489. if [ "${SEND_SMS}" = "YES" ] && [ -n "${sendsms}" ] && [ -n "${recipients}" ] && [ -n "${msg}" ]; then
  1490. # http://api.kavenegar.com/v1/{API-KEY}/sms/send.json
  1491. for phone in ${recipients}; do
  1492. errmessage=$($sendsms $phone "$msg" 2>&1)
  1493. errcode=$?
  1494. if [ ${errcode} -eq 0 ]; then
  1495. info "sent smstools3 SMS for: ${host} ${chart}.${name} is ${status} to '${user}'"
  1496. sent=$((sent + 1))
  1497. else
  1498. error "failed to send smstools3 SMS for: ${host} ${chart}.${name} is ${status} to '${user}' with error code ${errcode}: ${errmessage}."
  1499. fi
  1500. done
  1501. [ ${sent} -gt 0 ] && return 0
  1502. fi
  1503. return 1
  1504. }
  1505. # -----------------------------------------------------------------------------
  1506. # hangouts sender
  1507. send_hangouts() {
  1508. local rooms="${1}" httpcode sent=0 room color payload webhook
  1509. [ "${SEND_HANGOUTS}" != "YES" ] && return 1
  1510. case "${status}" in
  1511. WARNING) color="#ffa700" ;;
  1512. CRITICAL) color="#d62d20" ;;
  1513. CLEAR) color="#008744" ;;
  1514. *) color="#777777" ;;
  1515. esac
  1516. for room in ${rooms}; do
  1517. if [ -z "${HANGOUTS_WEBHOOK_URI[$room]}" ] ; then
  1518. info "Can't send Hangouts notification for: ${host} ${chart}.${name} to room ${room}. HANGOUTS_WEBHOOK_URI[$room] not defined"
  1519. else
  1520. webhook="${HANGOUTS_WEBHOOK_URI[$room]}"
  1521. payload="$(
  1522. cat <<EOF
  1523. {
  1524. "cards": [
  1525. {
  1526. "header": {
  1527. "title": "Netdata on ${host}",
  1528. "imageUrl": "${images_base_url}/images/banner-icon-144x144.png",
  1529. "imageStyle": "IMAGE"
  1530. },
  1531. "sections": [
  1532. {
  1533. "header": "<b>${host}</b>",
  1534. "widgets": [
  1535. {
  1536. "keyValue": {
  1537. "topLabel": "Status Message",
  1538. "content": "<b>${status_message}</b>",
  1539. "contentMultiline": "true",
  1540. "iconUrl": "${image}",
  1541. "onClick": {
  1542. "openLink": {
  1543. "url": "${goto_url}"
  1544. }
  1545. }
  1546. }
  1547. },
  1548. {
  1549. "keyValue": {
  1550. "topLabel": "${chart} | ${family}",
  1551. "content": "<font color=${color}>${alarm}</font>",
  1552. "contentMultiline": "true"
  1553. }
  1554. }
  1555. ]
  1556. },
  1557. {
  1558. "widgets": [
  1559. {
  1560. "textParagraph": {
  1561. "text": "<font color=\"#0057e7\">@ ${date}\n<b>${info}</b></font>"
  1562. }
  1563. }
  1564. ]
  1565. },
  1566. {
  1567. "widgets": [
  1568. {
  1569. "buttons": [
  1570. {
  1571. "textButton": {
  1572. "text": "Go to ${host}",
  1573. "onClick": {
  1574. "openLink": {
  1575. "url": "${goto_url}"
  1576. }
  1577. }
  1578. }
  1579. }
  1580. ]
  1581. }
  1582. ]
  1583. }
  1584. ]
  1585. }
  1586. ]
  1587. }
  1588. EOF
  1589. )"
  1590. httpcode=$(docurl -H "Content-Type: application/json" -X POST -d "${payload}" "${webhook}")
  1591. if [ "${httpcode}" = "200" ]; then
  1592. info "sent hangouts notification for: ${host} ${chart}.${name} is ${status} to '${room}'"
  1593. sent=$((sent + 1))
  1594. else
  1595. error "failed to send hangouts notification for: ${host} ${chart}.${name} is ${status} to '${room}', with HTTP error code ${httpcode}."
  1596. fi
  1597. fi
  1598. done
  1599. [ ${sent} -gt 0 ] && return 0
  1600. return 1
  1601. }
  1602. # -----------------------------------------------------------------------------
  1603. # Dynatrace sender
  1604. send_dynatrace() {
  1605. [ "${SEND_DYNATRACE}" != "YES" ] && return 1
  1606. local dynatrace_url="${DYNATRACE_SERVER}/e/${DYNATRACE_SPACE}/api/v1/events"
  1607. local description="NetData Notification for: ${host} ${chart}.${name} is ${status}"
  1608. local payload=""
  1609. payload=$(cat <<EOF
  1610. {
  1611. "title": "NetData Alarm from ${host}",
  1612. "source" : "${DYNATRACE_ANNOTATION_TYPE}",
  1613. "description" : "${description}",
  1614. "eventType": "${DYNATRACE_EVENT}",
  1615. "attachRules":{
  1616. "tagRule":[{
  1617. "meTypes":["HOST"],
  1618. "tags":["${DYNATRACE_TAG_VALUE}"]
  1619. }]
  1620. },
  1621. "customProperties":{
  1622. "description": "${description}"
  1623. }
  1624. }
  1625. EOF
  1626. )
  1627. # echo ${payload}
  1628. httpcode=$(docurl -X POST -H "Authorization: Api-token ${DYNATRACE_TOKEN}" -H "Content-Type: application/json" -d "${payload}" ${dynatrace_url})
  1629. ret=$?
  1630. if [ ${ret} -eq 0 ]; then
  1631. if [ "${httpcode}" = "200" ]; then
  1632. info "sent ${DYNATRACE_EVENT} to ${DYNATRACE_SERVER}"
  1633. return 0
  1634. else
  1635. warning "Dynatrace ${DYNATRACE_SERVER} responded ${httpcode} notification for: ${host} ${chart}.${name} is ${status} was not sent!"
  1636. return 1
  1637. fi
  1638. else
  1639. error "failed to sent ${DYNATRACE_EVENT} notification for: ${host} ${chart}.${name} is ${status} to ${DYNATRACE_SERVER} with error code ${ret}."
  1640. return 1
  1641. fi
  1642. }
  1643. # -----------------------------------------------------------------------------
  1644. # prepare the content of the notification
  1645. # the url to send the user on click
  1646. urlencode "${args_host}" >/dev/null
  1647. url_host="${REPLY}"
  1648. urlencode "${chart}" >/dev/null
  1649. url_chart="${REPLY}"
  1650. urlencode "${family}" >/dev/null
  1651. url_family="${REPLY}"
  1652. urlencode "${name}" >/dev/null
  1653. url_name="${REPLY}"
  1654. redirect_params="host=${url_host}&chart=${url_chart}&family=${url_family}&alarm=${url_name}&alarm_unique_id=${unique_id}&alarm_id=${alarm_id}&alarm_event_id=${event_id}&alarm_when=${when}"
  1655. GOTOCLOUD=0
  1656. if [ "${NETDATA_REGISTRY_URL}" == "https://registry.my-netdata.io" ]; then
  1657. if [ -z "${NETDATA_REGISTRY_UNIQUE_ID}" ]; then
  1658. if [ -f "@registrydir_POST@/netdata.public.unique.id" ]; then
  1659. NETDATA_REGISTRY_UNIQUE_ID="$(cat "@registrydir_POST@/netdata.public.unique.id")"
  1660. fi
  1661. fi
  1662. if [ -n "${NETDATA_REGISTRY_UNIQUE_ID}" ]; then
  1663. GOTOCLOUD=1
  1664. fi
  1665. fi
  1666. if [ ${GOTOCLOUD} -eq 0 ]; then
  1667. goto_url="${NETDATA_REGISTRY_URL}/goto-host-from-alarm.html?${redirect_params}"
  1668. else
  1669. goto_url="${NETDATA_REGISTRY_CLOUD_BASE_URL}/alarms/redirect?agentID=${NETDATA_REGISTRY_UNIQUE_ID}&${redirect_params}"
  1670. fi
  1671. # the severity of the alarm
  1672. severity="${status}"
  1673. # the time the alarm was raised
  1674. duration4human ${duration} >/dev/null
  1675. duration_txt="${REPLY}"
  1676. duration4human ${non_clear_duration} >/dev/null
  1677. non_clear_duration_txt="${REPLY}"
  1678. raised_for="(was ${old_status,,} for ${duration_txt})"
  1679. # the key status message
  1680. status_message="status unknown"
  1681. # the color of the alarm
  1682. color="grey"
  1683. # the alarm value
  1684. alarm="${name//_/ } = ${value_string}"
  1685. # the image of the alarm
  1686. image="${images_base_url}/images/banner-icon-144x144.png"
  1687. # prepare the title based on status
  1688. case "${status}" in
  1689. CRITICAL)
  1690. image="${images_base_url}/images/alert-128-red.png"
  1691. status_message="is critical"
  1692. color="#ca414b"
  1693. ;;
  1694. WARNING)
  1695. image="${images_base_url}/images/alert-128-orange.png"
  1696. status_message="needs attention"
  1697. color="#ffc107"
  1698. ;;
  1699. CLEAR)
  1700. image="${images_base_url}/images/check-mark-2-128-green.png"
  1701. status_message="recovered"
  1702. color="#77ca6d"
  1703. ;;
  1704. esac
  1705. if [ "${status}" = "CLEAR" ]; then
  1706. severity="Recovered from ${old_status}"
  1707. if [ ${non_clear_duration} -gt ${duration} ]; then
  1708. raised_for="(alarm was raised for ${non_clear_duration_txt})"
  1709. fi
  1710. # don't show the value when the status is CLEAR
  1711. # for certain alarms, this value might not have any meaning
  1712. alarm="${name//_/ } ${raised_for}"
  1713. elif { [ "${old_status}" = "WARNING" ] && [ "${status}" = "CRITICAL" ]; }; then
  1714. severity="Escalated to ${status}"
  1715. if [ ${non_clear_duration} -gt ${duration} ]; then
  1716. raised_for="(alarm is raised for ${non_clear_duration_txt})"
  1717. fi
  1718. elif { [ "${old_status}" = "CRITICAL" ] && [ "${status}" = "WARNING" ]; }; then
  1719. severity="Demoted to ${status}"
  1720. if [ ${non_clear_duration} -gt ${duration} ]; then
  1721. raised_for="(alarm is raised for ${non_clear_duration_txt})"
  1722. fi
  1723. else
  1724. raised_for=
  1725. fi
  1726. # prepare HTML versions of elements
  1727. info_html=
  1728. [ -n "${info}" ] && info_html=" <small><br/>${info}</small>"
  1729. raised_for_html=
  1730. [ -n "${raised_for}" ] && raised_for_html="<br/><small>${raised_for}</small>"
  1731. # -----------------------------------------------------------------------------
  1732. # send the slack notification
  1733. # slack aggregates posts from the same username
  1734. # so we use "${host} ${status}" as the bot username, to make them diff
  1735. send_slack "${SLACK_WEBHOOK_URL}" "${to_slack}"
  1736. SENT_SLACK=$?
  1737. # -----------------------------------------------------------------------------
  1738. # send the hangouts notification
  1739. # hangouts aggregates posts from the same room
  1740. # so we use "${host} ${status}" as the room, to make them diff
  1741. send_hangouts "${to_hangouts}"
  1742. SENT_HANGOUTS=$?
  1743. # -----------------------------------------------------------------------------
  1744. # send the Microsoft notification
  1745. # Microsoft team aggregates posts from the same username
  1746. # so we use "${host} ${status}" as the bot username, to make them diff
  1747. send_msteam "${MSTEAM_WEBHOOK_URL}" "${to_msteam}"
  1748. SENT_MSTEAM=$?
  1749. # -----------------------------------------------------------------------------
  1750. # send the rocketchat notification
  1751. # rocketchat aggregates posts from the same username
  1752. # so we use "${host} ${status}" as the bot username, to make them diff
  1753. send_rocketchat "${ROCKETCHAT_WEBHOOK_URL}" "${to_rocketchat}"
  1754. SENT_ROCKETCHAT=$?
  1755. # -----------------------------------------------------------------------------
  1756. # send the alerta notification
  1757. # alerta aggregates posts from the same username
  1758. # so we use "${host} ${status}" as the bot username, to make them diff
  1759. send_alerta "${ALERTA_WEBHOOK_URL}" "${to_alerta}"
  1760. SENT_ALERTA=$?
  1761. # -----------------------------------------------------------------------------
  1762. # send the flock notification
  1763. # flock aggregates posts from the same username
  1764. # so we use "${host} ${status}" as the bot username, to make them diff
  1765. send_flock "${FLOCK_WEBHOOK_URL}" "${to_flock}"
  1766. SENT_FLOCK=$?
  1767. # -----------------------------------------------------------------------------
  1768. # send the discord notification
  1769. # discord aggregates posts from the same username
  1770. # so we use "${host} ${status}" as the bot username, to make them diff
  1771. send_discord "${DISCORD_WEBHOOK_URL}" "${to_discord}"
  1772. SENT_DISCORD=$?
  1773. # -----------------------------------------------------------------------------
  1774. # send the pushover notification
  1775. send_pushover "${PUSHOVER_APP_TOKEN}" "${to_pushover}" "${when}" "${goto_url}" "${status}" "${host} ${status_message} - ${name//_/ } - ${chart}" "
  1776. <font color=\"${color}\"><b>${alarm}</b></font>${info_html}<br/>&nbsp;
  1777. <small><b>${chart}</b><br/>Chart<br/>&nbsp;</small>
  1778. <small><b>${family}</b><br/>Family<br/>&nbsp;</small>
  1779. <small><b>${severity}</b><br/>Severity<br/>&nbsp;</small>
  1780. <small><b>${date}${raised_for_html}</b><br/>Time<br/>&nbsp;</small>
  1781. <a href=\"${goto_url}\">View Netdata</a><br/>&nbsp;
  1782. <small><small>The source of this alarm is line ${src}</small></small>
  1783. "
  1784. SENT_PUSHOVER=$?
  1785. # -----------------------------------------------------------------------------
  1786. # send the pushbullet notification
  1787. send_pushbullet "${PUSHBULLET_ACCESS_TOKEN}" "${PUSHBULLET_SOURCE_DEVICE}" "${to_pushbullet}" "${goto_url}" "${host} ${status_message} - ${name//_/ } - ${chart}" "${alarm}\\n
  1788. Severity: ${severity}\\n
  1789. Chart: ${chart}\\n
  1790. Family: ${family}\\n
  1791. ${date}\\n
  1792. The source of this alarm is line ${src}"
  1793. SENT_PUSHBULLET=$?
  1794. # -----------------------------------------------------------------------------
  1795. # send the twilio SMS
  1796. send_twilio "${TWILIO_ACCOUNT_SID}" "${TWILIO_ACCOUNT_TOKEN}" "${TWILIO_NUMBER}" "${to_twilio}" "${host} ${status_message} - ${name//_/ } - ${chart}" "${alarm}
  1797. Severity: ${severity}
  1798. Chart: ${chart}
  1799. Family: ${family}
  1800. ${info}"
  1801. SENT_TWILIO=$?
  1802. # -----------------------------------------------------------------------------
  1803. # send the messagebird SMS
  1804. send_messagebird "${MESSAGEBIRD_ACCESS_KEY}" "${MESSAGEBIRD_NUMBER}" "${to_messagebird}" "${host} ${status_message} - ${name//_/ } - ${chart}" "${alarm}
  1805. Severity: ${severity}
  1806. Chart: ${chart}
  1807. Family: ${family}
  1808. ${info}"
  1809. SENT_MESSAGEBIRD=$?
  1810. # -----------------------------------------------------------------------------
  1811. # send the kavenegar SMS
  1812. send_kavenegar "${KAVENEGAR_API_KEY}" "${KAVENEGAR_SENDER}" "${to_kavenegar}" "${host} ${status_message} - ${name//_/ } - ${chart}" "${alarm}
  1813. Severity: ${severity}
  1814. Chart: ${chart}
  1815. Family: ${family}
  1816. ${info}"
  1817. SENT_KAVENEGAR=$?
  1818. # -----------------------------------------------------------------------------
  1819. # send the telegram.org message
  1820. # https://core.telegram.org/bots/api#formatting-options
  1821. send_telegram "${TELEGRAM_BOT_TOKEN}" "${to_telegram}" "${host} ${status_message} - <b>${name//_/ }</b>
  1822. ${chart} (${family})
  1823. <a href=\"${goto_url}\">${alarm}</a>
  1824. <i>${info}</i>"
  1825. SENT_TELEGRAM=$?
  1826. # -----------------------------------------------------------------------------
  1827. # send the kafka message
  1828. send_kafka
  1829. SENT_KAFKA=$?
  1830. # -----------------------------------------------------------------------------
  1831. # send the pagerduty.com message
  1832. send_pd "${to_pd}"
  1833. SENT_PD=$?
  1834. # -----------------------------------------------------------------------------
  1835. # send the fleep message
  1836. send_fleep "${to_fleep}"
  1837. SENT_FLEEP=$?
  1838. # -----------------------------------------------------------------------------
  1839. # send the Prowl message
  1840. send_prowl "${to_prowl}"
  1841. SENT_PROWL=$?
  1842. # -----------------------------------------------------------------------------
  1843. # send the irc message
  1844. send_irc "${IRC_NICKNAME}" "${IRC_REALNAME}" "${to_irc}" "${IRC_NETWORK}" "${host}" "${host} ${status_message} - ${name//_/ } - ${chart} ----- ${alarm}
  1845. Severity: ${severity}
  1846. Chart: ${chart}
  1847. Family: ${family}
  1848. ${info}"
  1849. SENT_IRC=$?
  1850. # -----------------------------------------------------------------------------
  1851. # send the SMS message with smstools3
  1852. send_sms "${to_sms}"
  1853. SENT_SMS=$?
  1854. # -----------------------------------------------------------------------------
  1855. # send the custom message
  1856. send_custom() {
  1857. # is it enabled?
  1858. [ "${SEND_CUSTOM}" != "YES" ] && return 1
  1859. # do we have any sender?
  1860. [ -z "${1}" ] && return 1
  1861. # call the custom_sender function
  1862. custom_sender "${@}"
  1863. }
  1864. send_custom "${to_custom}"
  1865. SENT_CUSTOM=$?
  1866. # -----------------------------------------------------------------------------
  1867. # send hipchat message
  1868. send_hipchat "${HIPCHAT_AUTH_TOKEN}" "${to_hipchat}" " \
  1869. ${host} ${status_message}<br/> \
  1870. <b>${alarm}</b> ${info_html}<br/> \
  1871. <b>${chart}</b> (family <b>${family}</b>)<br/> \
  1872. <b>${date}${raised_for_html}</b><br/> \
  1873. <a href=\\\"${goto_url}\\\">View netdata dashboard</a> \
  1874. (source of alarm ${src}) \
  1875. "
  1876. SENT_HIPCHAT=$?
  1877. # -----------------------------------------------------------------------------
  1878. # send the Amazon SNS message
  1879. send_awssns "${to_awssns}"
  1880. SENT_AWSSNS=$?
  1881. # -----------------------------------------------------------------------------
  1882. # send the syslog message
  1883. send_syslog "${to_syslog}"
  1884. SENT_SYSLOG=$?
  1885. # -----------------------------------------------------------------------------
  1886. # send the email
  1887. IFS='' read -r -d '' email_plaintext_part <<EOF
  1888. Content-Type: text/plain; encoding=${EMAIL_CHARSET}
  1889. Content-Disposition: inline
  1890. Content-Transfer-Encoding: 8bit
  1891. ${host} ${status_message}
  1892. ${alarm} ${info}
  1893. ${raised_for}
  1894. Chart : ${chart}
  1895. Family : ${family}
  1896. Severity: ${severity}
  1897. URL : ${goto_url}
  1898. Source : ${src}
  1899. Date : ${date}
  1900. Notification generated on ${host}
  1901. Evaluated Expression : ${calc_expression}
  1902. Expression Variables : ${calc_param_values}
  1903. The host has ${total_warnings} WARNING and ${total_critical} CRITICAL alarm(s) raised.
  1904. EOF
  1905. if [[ "${EMAIL_PLAINTEXT_ONLY}" == "YES" ]]; then
  1906. send_email <<EOF
  1907. To: ${to_email}
  1908. Subject: ${host} ${status_message} - ${name//_/ } - ${chart}
  1909. MIME-Version: 1.0
  1910. Content-Type: multipart/alternative; boundary="multipart-boundary"
  1911. ${email_thread_headers}
  1912. This is a MIME-encoded multipart message
  1913. --multipart-boundary
  1914. ${email_plaintext_part}
  1915. --multipart-boundary--
  1916. EOF
  1917. else
  1918. IFS='' read -r -d '' email_html_part <<EOF
  1919. Content-Type: text/html; encoding=${EMAIL_CHARSET}
  1920. Content-Disposition: inline
  1921. Content-Transfer-Encoding: 8bit
  1922. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  1923. <html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
  1924. <body style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; width: 100% !important; min-height: 100%; line-height: 1.6; background: #f6f6f6; margin:0; padding: 0;">
  1925. <table>
  1926. <tbody>
  1927. <tr>
  1928. <td style="vertical-align: top;" valign="top"></td>
  1929. <td width="700" style="vertical-align: top; display: block !important; max-width: 700px !important; clear: both !important; margin: 0 auto; padding: 0;" valign="top">
  1930. <div style="max-width: 700px; display: block; margin: 0 auto; padding: 20px;">
  1931. <table width="100%" cellpadding="0" cellspacing="0" style="background: #fff; border: 1px solid #e9e9e9;">
  1932. <tbody>
  1933. <tr>
  1934. <td bgcolor="#eee" style="padding: 5px 20px 5px 20px; background-color: #eee;">
  1935. <div style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 20px; color: #777; font-weight: bold;">netdata notification</div>
  1936. </td>
  1937. </tr>
  1938. <tr>
  1939. <td bgcolor="${color}" style="font-size: 16px; vertical-align: top; font-weight: 400; text-align: center; margin: 0; padding: 10px; color: #ffffff; background: ${color} !important; border: 1px solid ${color}; border-top-color: ${color};" align="center" valign="top">
  1940. <h1 style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-weight: 400; margin: 0;">${host} ${status_message}</h1>
  1941. </td>
  1942. </tr>
  1943. <tr>
  1944. <td style="vertical-align: top;" valign="top">
  1945. <div style="margin: 0; padding: 20px; max-width: 700px;">
  1946. <table width="100%" cellpadding="0" cellspacing="0" style="max-width:700px">
  1947. <tbody>
  1948. <tr>
  1949. <td style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 18px; vertical-align: top; margin: 0; padding:0 0 20px;" align="left" valign="top">
  1950. <span>${chart}</span>
  1951. <span style="display: block; color: #666666; font-size: 12px; font-weight: 300; line-height: 1; text-transform: uppercase;">Chart</span>
  1952. </td>
  1953. </tr>
  1954. <tr style="margin: 0; padding: 0;">
  1955. <td style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 18px; vertical-align: top; margin: 0; padding: 0 0 20px;" align="left" valign="top">
  1956. <span><b>${alarm}</b>${info_html}</span>
  1957. <span style="display: block; color: #666666; font-size: 12px; font-weight: 300; line-height: 1; text-transform: uppercase;">Alarm</span>
  1958. </td>
  1959. </tr>
  1960. <tr>
  1961. <td style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 18px; vertical-align: top; margin: 0; padding: 0 0 20px;" align="left" valign="top">
  1962. <span>${family}</span>
  1963. <span style="display: block; color: #666666; font-size: 12px; font-weight: 300; line-height: 1; text-transform: uppercase;">Family</span>
  1964. </td>
  1965. </tr>
  1966. <tr style="margin: 0; padding: 0;">
  1967. <td style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 18px; vertical-align: top; margin: 0; padding: 0 0 20px;" align="left" valign="top">
  1968. <span>${severity}</span>
  1969. <span style="display: block; color: #666666; font-size: 12px; font-weight: 300; line-height: 1; text-transform: uppercase;">Severity</span>
  1970. </td>
  1971. </tr>
  1972. <tr style="margin: 0; padding: 0;">
  1973. <td style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 18px; vertical-align: top; margin: 0; padding: 0 0 20px;" align="left" valign="top"><span>${date}</span>
  1974. <span>${raised_for_html}</span> <span style="display: block; color: #666666; font-size: 12px; font-weight: 300; line-height: 1; text-transform: uppercase;">Time</span>
  1975. </td>
  1976. </tr>
  1977. <tr style="margin: 0; padding: 0;">
  1978. <td style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 18px; vertical-align: top; margin: 0; padding: 0 0 20px;" align="left" valign="top">
  1979. <span>${calc_expression}</span>
  1980. <span style="display: block; color: #666666; font-size: 12px; font-weight: 300; line-height: 1; text-transform: uppercase;">Evaluated Expression</span>
  1981. </td>
  1982. </tr>
  1983. <tr style="margin: 0; padding: 0;">
  1984. <td style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 18px; vertical-align: top; margin: 0; padding: 0 0 20px;" align="left" valign="top">
  1985. <span>${calc_param_values}</span>
  1986. <span style="display: block; color: #666666; font-size: 12px; font-weight: 300; line-height: 1; text-transform: uppercase;">Expression Variables</span>
  1987. </td>
  1988. </tr>
  1989. <tr style="margin: 0; padding: 0;">
  1990. <td style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 18px; vertical-align: top; margin: 0; padding: 0 0 20px;" align="left" valign="top">
  1991. The host has ${total_warnings} WARNING and ${total_critical} CRITICAL alarm(s) raised.
  1992. </td>
  1993. </tr>
  1994. <tr style="margin: 0; padding: 0;">
  1995. <td style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 18px; vertical-align: top; margin: 0; padding: 0 0 20px;">
  1996. <a href="${goto_url}" style="font-size: 14px; color: #ffffff; text-decoration: none; line-height: 1.5; font-weight: bold; text-align: center; display: inline-block; text-transform: capitalize; background: #35568d; border-width: 1px; border-style: solid; border-color: #2b4c86; margin: 0; padding: 10px 15px;" target="_blank">View Netdata</a>
  1997. </td>
  1998. </tr>
  1999. <tr style="text-align: center; margin: 0; padding: 0;">
  2000. <td style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 11px; vertical-align: top; margin: 0; padding: 10px 0 0 0; color: #666666;" align="center" valign="bottom">The source of this alarm is line <code>${src}</code><br/>(alarms are configurable, edit this file to adapt the alarm to your needs)
  2001. </td>
  2002. </tr>
  2003. <tr style="text-align: center; margin: 0; padding: 0;">
  2004. <td style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; vertical-align: top; margin:0; padding: 20px 0 0 0; color: #666666; border-top: 1px solid #f0f0f0;" align="center" valign="bottom">Sent by
  2005. <a href="https://mynetdata.io/" target="_blank">netdata</a>, the real-time performance and health monitoring, on <code>${host}</code>.
  2006. </td>
  2007. </tr>
  2008. </tbody>
  2009. </table>
  2010. </div>
  2011. </td>
  2012. </tr>
  2013. </tbody>
  2014. </table>
  2015. </div>
  2016. </td>
  2017. </tr>
  2018. </tbody>
  2019. </table>
  2020. </body>
  2021. </html>
  2022. EOF
  2023. send_email <<EOF
  2024. To: ${to_email}
  2025. Subject: ${host} ${status_message} - ${name//_/ } - ${chart}
  2026. MIME-Version: 1.0
  2027. Content-Type: multipart/alternative; boundary="multipart-boundary"
  2028. ${email_thread_headers}
  2029. This is a MIME-encoded multipart message
  2030. --multipart-boundary
  2031. ${email_plaintext_part}
  2032. --multipart-boundary
  2033. ${email_html_part}
  2034. --multipart-boundary--
  2035. EOF
  2036. fi
  2037. SENT_EMAIL=$?
  2038. # -----------------------------------------------------------------------------
  2039. # send the EVENT to Dynatrace
  2040. send_dynatrace "${host}" "${chart}" "${name}" "${status}"
  2041. SENT_DYNATRACE=$?
  2042. # -----------------------------------------------------------------------------
  2043. # let netdata know
  2044. for state in "${SENT_EMAIL}" \
  2045. "${SENT_PUSHOVER}" \
  2046. "${SENT_TELEGRAM}" \
  2047. "${SENT_SLACK}" \
  2048. "${SENT_HANGOUTS}" \
  2049. "${SENT_ROCKETCHAT}" \
  2050. "${SENT_ALERTA}" \
  2051. "${SENT_FLOCK}" \
  2052. "${SENT_DISCORD}" \
  2053. "${SENT_TWILIO}" \
  2054. "${SENT_HIPCHAT}" \
  2055. "${SENT_MESSAGEBIRD}" \
  2056. "${SENT_KAVENEGAR}" \
  2057. "${SENT_PUSHBULLET}" \
  2058. "${SENT_KAFKA}" \
  2059. "${SENT_PD}" \
  2060. "${SENT_FLEEP}" \
  2061. "${SENT_PROWL}" \
  2062. "${SENT_CUSTOM}" \
  2063. "${SENT_IRC}" \
  2064. "${SENT_AWSSNS}" \
  2065. "${SENT_SYSLOG}" \
  2066. "${SENT_SMS}" \
  2067. "${SENT_MSTEAM}" \
  2068. "${SENT_DYNATRACE}"; do
  2069. if [ "${state}" -eq 0 ]; then
  2070. # we sent something
  2071. exit 0
  2072. fi
  2073. done
  2074. # we did not send anything
  2075. exit 1