alarm-notify.sh.in 75 KB

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