alarm-notify.sh.in 72 KB

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