alarm-notify.sh.in 131 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492
  1. #!/usr/bin/env bash
  2. #shellcheck source=/dev/null disable=SC2086,SC2154
  3. # netdata
  4. # real-time performance and health monitoring, done right!
  5. # (C) 2017 Costa Tsaousis <costa@tsaousis.gr>
  6. # SPDX-License-Identifier: GPL-3.0-or-later
  7. #
  8. # Script to send alarm notifications for netdata
  9. #
  10. # Features:
  11. # - multiple notification methods
  12. # - multiple roles per alarm
  13. # - multiple recipients per role
  14. # - severity filtering per recipient
  15. #
  16. # Supported notification methods:
  17. # - emails by @ktsaou
  18. # - slack.com notifications by @ktsaou
  19. # - alerta.io notifications by @kattunga
  20. # - discordapp.com notifications by @lowfive
  21. # - pushover.net notifications by @ktsaou
  22. # - pushbullet.com push notifications by Tiago Peralta @tperalta82 #1070
  23. # - telegram.org notifications by @hashworks #1002
  24. # - twilio.com notifications by Levi Blaney @shadycuz #1211
  25. # - kafka notifications by @ktsaou #1342
  26. # - pagerduty.com notifications by Jim Cooley @jimcooley #1373
  27. # - messagebird.com notifications by @tech_no_logical #1453
  28. # - hipchat notifications by @ktsaou #1561
  29. # - fleep notifications by @Ferroin
  30. # - prowlapp.com notifications by @Ferroin
  31. # - irc notifications by @manosf
  32. # - custom notifications by @ktsaou
  33. # - syslog messages by @Ferroin
  34. # - Microsoft Team notification by @tioumen
  35. # - RocketChat notifications by @Hermsi1337 #3777
  36. # - Google Hangouts Chat notifications by @EnzoAkira and @hendrikhofstadt
  37. # - Dynatrace Event by @illumine
  38. # - Stackpulse Event by @thiagoftsm
  39. # - Opsgenie by @thiaoftsm #9858
  40. # -----------------------------------------------------------------------------
  41. # testing notifications
  42. if { [ "${1}" = "test" ] || [ "${2}" = "test" ]; } && [ "${#}" -le 2 ]; then
  43. if [ "${2}" = "test" ]; then
  44. recipient="${1}"
  45. else
  46. recipient="${2}"
  47. fi
  48. [ -z "${recipient}" ] && recipient="sysadmin"
  49. id=1
  50. last="CLEAR"
  51. test_res=0
  52. for x in "WARNING" "CRITICAL" "CLEAR"; do
  53. echo >&2
  54. echo >&2 "# SENDING TEST ${x} ALARM TO ROLE: ${recipient}"
  55. "${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
  56. #shellcheck disable=SC2181
  57. if [ $? -ne 0 ]; then
  58. echo >&2 "# FAILED"
  59. test_res=1
  60. else
  61. echo >&2 "# OK"
  62. fi
  63. last="${x}"
  64. id=$((id + 1))
  65. done
  66. exit $test_res
  67. fi
  68. export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/sbin"
  69. export LC_ALL=C
  70. # -----------------------------------------------------------------------------
  71. PROGRAM_NAME="$(basename "${0}")"
  72. logdate() {
  73. date "+%Y-%m-%d %H:%M:%S"
  74. }
  75. log() {
  76. local status="${1}"
  77. shift
  78. echo >&2 "$(logdate): ${PROGRAM_NAME}: ${status}: ${*}"
  79. }
  80. warning() {
  81. log WARNING "${@}"
  82. }
  83. error() {
  84. log ERROR "${@}"
  85. }
  86. info() {
  87. log INFO "${@}"
  88. }
  89. fatal() {
  90. log FATAL "${@}"
  91. exit 1
  92. }
  93. debug=${NETDATA_ALARM_NOTIFY_DEBUG-0}
  94. debug() {
  95. [ "${debug}" = "1" ] && log DEBUG "${@}"
  96. }
  97. docurl() {
  98. if [ -z "${curl}" ]; then
  99. error "${curl} is unset."
  100. return 1
  101. fi
  102. if [ "${debug}" = "1" ]; then
  103. echo >&2 "--- BEGIN curl command ---"
  104. printf >&2 "%q " ${curl} "${@}"
  105. echo >&2
  106. echo >&2 "--- END curl command ---"
  107. local out code ret
  108. out=$(mktemp /tmp/netdata-health-alarm-notify-XXXXXXXX)
  109. code=$(${curl} ${curl_options} --write-out "%{http_code}" --output "${out}" --silent --show-error "${@}")
  110. ret=$?
  111. echo >&2 "--- BEGIN received response ---"
  112. cat >&2 "${out}"
  113. echo >&2
  114. echo >&2 "--- END received response ---"
  115. echo >&2 "RECEIVED HTTP RESPONSE CODE: ${code}"
  116. rm "${out}"
  117. echo "${code}"
  118. return ${ret}
  119. fi
  120. ${curl} ${curl_options} --write-out "%{http_code}" --output /dev/null --silent --show-error "${@}"
  121. return $?
  122. }
  123. # -----------------------------------------------------------------------------
  124. # List of all the notification mechanisms we support.
  125. # Used in a couple of places to write more compact code.
  126. method_names="
  127. email
  128. pushover
  129. pushbullet
  130. telegram
  131. slack
  132. alerta
  133. flock
  134. discord
  135. hipchat
  136. twilio
  137. messagebird
  138. pd
  139. fleep
  140. syslog
  141. custom
  142. msteams
  143. kavenegar
  144. prowl
  145. irc
  146. awssns
  147. rocketchat
  148. sms
  149. hangouts
  150. dynatrace
  151. matrix
  152. "
  153. # -----------------------------------------------------------------------------
  154. # this is to be overwritten by the config file
  155. custom_sender() {
  156. info "not sending custom notification for ${status} of '${host}.${chart}.${name}'"
  157. }
  158. # -----------------------------------------------------------------------------
  159. # check for BASH v4+ (required for associative arrays)
  160. if [ ${BASH_VERSINFO[0]} -lt 4 ]; then
  161. fatal "BASH version 4 or later is required (this is ${BASH_VERSION})."
  162. fi
  163. # -----------------------------------------------------------------------------
  164. # defaults to allow running this script by hand
  165. [ -z "${NETDATA_USER_CONFIG_DIR}" ] && NETDATA_USER_CONFIG_DIR="@configdir_POST@"
  166. [ -z "${NETDATA_STOCK_CONFIG_DIR}" ] && NETDATA_STOCK_CONFIG_DIR="@libconfigdir_POST@"
  167. [ -z "${NETDATA_CACHE_DIR}" ] && NETDATA_CACHE_DIR="@cachedir_POST@"
  168. [ -z "${NETDATA_REGISTRY_URL}" ] && NETDATA_REGISTRY_URL="https://registry.my-netdata.io"
  169. [ -z "${NETDATA_REGISTRY_CLOUD_BASE_URL}" ] && NETDATA_REGISTRY_CLOUD_BASE_URL="https://app.netdata.cloud"
  170. # -----------------------------------------------------------------------------
  171. # parse command line parameters
  172. if [[ ${1} = "unittest" ]]; then
  173. unittest=1 # enable unit testing mode
  174. roles="${2}" # the role that should be used for unit testing
  175. cfgfile="${3}" # the location of the config file to use for unit testing
  176. status="${4}" # the current status : REMOVED, UNINITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL
  177. old_status="${5}" # the previous status: REMOVED, UNINITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL
  178. elif [[ ${1} = "dump_methods" ]]; then
  179. dump_methods=1
  180. status="WARNING"
  181. else
  182. roles="${1}" # the roles that should be notified for this event
  183. args_host="${2}" # the host generated this event
  184. unique_id="${3}" # the unique id of this event
  185. alarm_id="${4}" # the unique id of the alarm that generated this event
  186. event_id="${5}" # the incremental id of the event, for this alarm id
  187. when="${6}" # the timestamp this event occurred
  188. name="${7}" # the name of the alarm, as given in netdata health.d entries
  189. chart="${8}" # the name of the chart (type.id)
  190. family="${9}" # the family of the chart
  191. status="${10}" # the current status : REMOVED, UNINITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL
  192. old_status="${11}" # the previous status: REMOVED, UNINITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL
  193. value="${12}" # the current value of the alarm
  194. old_value="${13}" # the previous value of the alarm
  195. src="${14}" # the line number and file the alarm has been configured
  196. duration="${15}" # the duration in seconds of the previous alarm state
  197. non_clear_duration="${16}" # the total duration in seconds this is/was non-clear
  198. units="${17}" # the units of the value
  199. info="${18}" # a short description of the alarm
  200. value_string="${19}" # friendly value (with units)
  201. # shellcheck disable=SC2034
  202. # variable is unused, but https://github.com/netdata/netdata/pull/5164#discussion_r255572947
  203. old_value_string="${20}" # friendly old value (with units), previously named "old_value_string"
  204. calc_expression="${21}" # contains the expression that was evaluated to trigger the alarm
  205. calc_param_values="${22}" # the values of the parameters in the expression, at the time of the evaluation
  206. total_warnings="${23}" # Total number of alarms in WARNING state
  207. total_critical="${24}" # Total number of alarms in CRITICAL state
  208. total_warn_alarms="${25}" # List of alarms in warning state
  209. total_crit_alarms="${26}" # List of alarms in critical state
  210. classification="${27}" # The class field from .conf files
  211. edit_command_line="${28}" # The command to edit the alarm, with the line number
  212. sender_host="${29}" # The host sending this notification
  213. fi
  214. # -----------------------------------------------------------------------------
  215. # find a suitable hostname to use, if netdata did not supply a hostname
  216. if [ -z ${args_host} ]; then
  217. this_host=$(hostname -s 2>/dev/null)
  218. host="${this_host}"
  219. args_host="${this_host}"
  220. else
  221. host="${args_host}"
  222. fi
  223. # -----------------------------------------------------------------------------
  224. # Do the same for sender_host (find a suitable hostname to use, if netdata did not supply a hostname)
  225. if [ -z ${sender_host} ]; then
  226. this_host=$(hostname -s 2>/dev/null)
  227. s_host="${this_host}"
  228. sender_host="${this_host}"
  229. else
  230. s_host="${sender_host}"
  231. fi
  232. # -----------------------------------------------------------------------------
  233. # screen statuses we don't need to send a notification
  234. # don't do anything if this is not WARNING, CRITICAL or CLEAR
  235. if [ "${status}" != "WARNING" ] && [ "${status}" != "CRITICAL" ] && [ "${status}" != "CLEAR" ]; then
  236. info "not sending notification for ${status} of '${host}.${chart}.${name}'"
  237. exit 1
  238. fi
  239. # don't do anything if this is CLEAR, but it was not WARNING or CRITICAL
  240. if [ "${clear_alarm_always}" != "YES" ] && [ "${old_status}" != "WARNING" ] && [ "${old_status}" != "CRITICAL" ] && [ "${status}" = "CLEAR" ]; then
  241. info "not sending notification for ${status} of '${host}.${chart}.${name}' (last status was ${old_status})"
  242. exit 1
  243. fi
  244. # -----------------------------------------------------------------------------
  245. # load configuration
  246. # By default fetch images from the global public registry.
  247. # This is required by default, since all notification methods need to download
  248. # images via the Internet, and private registries might not be reachable.
  249. # This can be overwritten at the configuration file.
  250. images_base_url="https://registry.my-netdata.io"
  251. # curl options to use
  252. curl_options=""
  253. # hostname handling
  254. use_fqdn="NO"
  255. # needed commands
  256. # if empty they will be searched in the system path
  257. curl=
  258. sendmail=
  259. # enable / disable features
  260. for method_name in ${method_names^^}; do
  261. declare SEND_${method_name}="YES"
  262. declare DEFAULT_RECIPIENT_${method_name}
  263. done
  264. for method_name in ${method_names}; do
  265. declare -A role_recipients_${method_name}
  266. done
  267. # slack configs
  268. SLACK_WEBHOOK_URL=
  269. # Microsoft Teams configs
  270. MSTEAMS_WEBHOOK_URL=
  271. # Legacy Microsoft Teams configs for backwards compatability:
  272. declare -A role_recipients_msteam
  273. # rocketchat configs
  274. ROCKETCHAT_WEBHOOK_URL=
  275. # alerta configs
  276. ALERTA_WEBHOOK_URL=
  277. ALERTA_API_KEY=
  278. # flock configs
  279. FLOCK_WEBHOOK_URL=
  280. # discord configs
  281. DISCORD_WEBHOOK_URL=
  282. # pushover configs
  283. PUSHOVER_APP_TOKEN=
  284. # pushbullet configs
  285. PUSHBULLET_ACCESS_TOKEN=
  286. PUSHBULLET_SOURCE_DEVICE=
  287. # twilio configs
  288. TWILIO_ACCOUNT_SID=
  289. TWILIO_ACCOUNT_TOKEN=
  290. TWILIO_NUMBER=
  291. # hipchat configs
  292. HIPCHAT_SERVER=
  293. HIPCHAT_AUTH_TOKEN=
  294. # messagebird configs
  295. MESSAGEBIRD_ACCESS_KEY=
  296. MESSAGEBIRD_NUMBER=
  297. # kavenegar configs
  298. KAVENEGAR_API_KEY=
  299. KAVENEGAR_SENDER=
  300. # telegram configs
  301. TELEGRAM_BOT_TOKEN=
  302. # kafka configs
  303. SEND_KAFKA="YES"
  304. KAFKA_URL=
  305. KAFKA_SENDER_IP=
  306. # pagerduty.com configs
  307. PD_SERVICE_KEY=
  308. USE_PD_VERSION=
  309. # fleep.io configs
  310. FLEEP_SENDER="${host}"
  311. # Amazon SNS configs
  312. AWSSNS_MESSAGE_FORMAT=
  313. # Matrix configs
  314. MATRIX_HOMESERVER=
  315. MATRIX_ACCESSTOKEN=
  316. # syslog configs
  317. SYSLOG_FACILITY=
  318. # email configs
  319. EMAIL_SENDER=
  320. EMAIL_CHARSET=$(locale charmap 2>/dev/null)
  321. EMAIL_THREADING=
  322. EMAIL_PLAINTEXT_ONLY=
  323. # irc configs
  324. IRC_NICKNAME=
  325. IRC_REALNAME=
  326. IRC_NETWORK=
  327. IRC_PORT=6667
  328. # hangouts configs
  329. declare -A HANGOUTS_WEBHOOK_URI
  330. declare -A HANGOUTS_WEBHOOK_THREAD
  331. # dynatrace configs
  332. DYNATRACE_SPACE=
  333. DYNATRACE_SERVER=
  334. DYNATRACE_TOKEN=
  335. DYNATRACE_TAG_VALUE=
  336. DYNATRACE_ANNOTATION_TYPE=
  337. DYNATRACE_EVENT=
  338. SEND_DYNATRACE=
  339. # stackpulse configs
  340. STACKPULSE_WEBHOOK=
  341. # opsgenie configs
  342. OPSGENIE_API_KEY=
  343. # load the stock and user configuration files
  344. # these will overwrite the variables above
  345. if [ ${unittest} ]; then
  346. if source "${cfgfile}"; then
  347. error "Failed to load requested config file."
  348. exit 1
  349. fi
  350. else
  351. for CONFIG in "${NETDATA_STOCK_CONFIG_DIR}/health_alarm_notify.conf" "${NETDATA_USER_CONFIG_DIR}/health_alarm_notify.conf"; do
  352. if [ -f "${CONFIG}" ]; then
  353. debug "Loading config file '${CONFIG}'..."
  354. source "${CONFIG}" || error "Failed to load config file '${CONFIG}'."
  355. else
  356. warning "Cannot find file '${CONFIG}'."
  357. fi
  358. done
  359. fi
  360. OPSGENIE_API_URL=${OPSGENIE_API_URL:-"https://api.opsgenie.com"}
  361. # If we didn't autodetect the character set for e-mail and it wasn't
  362. # set by the user, we need to set it to a reasonable default. UTF-8
  363. # should be correct for almost all modern UNIX systems.
  364. if [ -z ${EMAIL_CHARSET} ]; then
  365. EMAIL_CHARSET="UTF-8"
  366. fi
  367. # If we've been asked to use FQDN's for the URL's in the alarm, do so,
  368. # unless we're sending an alarm for a child system which we can't get the
  369. # FQDN of easily.
  370. if [ "${use_fqdn}" = "YES" ] && [ "${host}" = "$(hostname -s 2>/dev/null)" ]; then
  371. host="$(hostname -f 2>/dev/null)"
  372. fi
  373. # -----------------------------------------------------------------------------
  374. # migrate old Microsoft Teams configuration keys after loading configuration
  375. msteams_migration() {
  376. SEND_MSTEAMS=${SEND_MSTEAM:-$SEND_MSTEAMS}
  377. unset -v SEND_MSTEAM
  378. DEFAULT_RECIPIENT_MSTEAMS=${DEFAULT_RECIPIENT_MSTEAM:-$DEFAULT_RECIPIENT_MSTEAMS}
  379. MSTEAMS_WEBHOOK_URL=${MSTEAM_WEBHOOK_URL:-$MSTEAMS_WEBHOOK_URL}
  380. MSTEAMS_ICON_DEFAULT=${MSTEAM_ICON_DEFAULT:-$MSTEAMS_ICON_DEFAULT}
  381. MSTEAMS_ICON_CLEAR=${MSTEAM_ICON_CLEAR:-$MSTEAMS_ICON_CLEAR}
  382. MSTEAMS_ICON_WARNING=${MSTEAM_ICON_WARNING:-$MSTEAMS_ICON_WARNING}
  383. MSTEAMS_ICON_CRITICAL=${MSTEAM_ICON_CRITICAL:-$MSTEAMS_ICON_CRITICAL}
  384. MSTEAMS_COLOR_DEFAULT=${MSTEAM_COLOR_DEFAULT:-$MSTEAMS_COLOR_DEFAULT}
  385. MSTEAMS_COLOR_CLEAR=${MSTEAM_COLOR_CLEAR:-$MSTEAMS_COLOR_CLEAR}
  386. MSTEAMS_COLOR_WARNING=${MSTEAM_COLOR_WARNING:-$MSTEAMS_COLOR_WARNING}
  387. MSTEAMS_COLOR_CRITICAL=${MSTEAM_COLOR_CRITICAL:-$MSTEAMS_COLOR_CRITICAL}
  388. # migrate role specific recipients:
  389. for key in "${!role_recipients_msteam[@]}"; do
  390. # Disable check, if role_recipients_msteams is ever used:
  391. # The role_recipients_$method are created and used programmatically
  392. # by iterating over $methods. shellcheck therefore doesn't realize
  393. # that role_recipients_msteams is actually used in the block
  394. # "find the recipients' addresses per method".
  395. # shellcheck disable=SC2034
  396. role_recipients_msteams["$key"]="${role_recipients_msteam["$key"]}"
  397. done
  398. }
  399. msteams_migration
  400. # -----------------------------------------------------------------------------
  401. # filter a recipient based on alarm event severity
  402. filter_recipient_by_criticality() {
  403. local method="${1}" x="${2}" r s
  404. shift
  405. r="${x/|*/}" # the recipient
  406. s="${x/*|/}" # the severity required for notifying this recipient
  407. # no severity filtering for this person
  408. [ "${r}" = "${s}" ] && return 0
  409. # the severity is invalid
  410. s="${s^^}"
  411. if [ "${s}" != "CRITICAL" ]; then
  412. error "SEVERITY FILTERING for ${x} VIA ${method}: invalid severity '${s,,}', only 'critical' is supported."
  413. return 0
  414. fi
  415. # create the status tracking directory for this user
  416. [ ! -d "${NETDATA_CACHE_DIR}/alarm-notify/${method}/${r}" ] &&
  417. mkdir -p "${NETDATA_CACHE_DIR}/alarm-notify/${method}/${r}"
  418. case "${status}" in
  419. CRITICAL)
  420. # make sure he will get future notifications for this alarm too
  421. touch "${NETDATA_CACHE_DIR}/alarm-notify/${method}/${r}/${alarm_id}"
  422. debug "SEVERITY FILTERING for ${x} VIA ${method}: ALLOW: the alarm is CRITICAL (will now receive next status change)"
  423. return 0
  424. ;;
  425. WARNING)
  426. if [ -f "${NETDATA_CACHE_DIR}/alarm-notify/${method}/${r}/${alarm_id}" ]; then
  427. # we do not remove the file, so that he will get future notifications of this alarm
  428. debug "SEVERITY FILTERING for ${x} VIA ${method}: ALLOW: recipient has been notified for this alarm in the past (will still receive next status change)"
  429. return 0
  430. fi
  431. ;;
  432. *)
  433. if [ -f "${NETDATA_CACHE_DIR}/alarm-notify/${method}/${r}/${alarm_id}" ]; then
  434. # remove the file, so that he will only receive notifications for CRITICAL states for this alarm
  435. rm "${NETDATA_CACHE_DIR}/alarm-notify/${method}/${r}/${alarm_id}"
  436. debug "SEVERITY FILTERING for ${x} VIA ${method}: ALLOW: recipient has been notified for this alarm (will only receive CRITICAL notifications from now on)"
  437. return 0
  438. fi
  439. ;;
  440. esac
  441. debug "SEVERITY FILTERING for ${x} VIA ${method}: BLOCK: recipient should not receive this notification"
  442. return 1
  443. }
  444. # -----------------------------------------------------------------------------
  445. # verify the delivery methods supported
  446. # check slack
  447. [ -z "${SLACK_WEBHOOK_URL}" ] && SEND_SLACK="NO"
  448. # check rocketchat
  449. [ -z "${ROCKETCHAT_WEBHOOK_URL}" ] && SEND_ROCKETCHAT="NO"
  450. # check alerta
  451. [ -z "${ALERTA_WEBHOOK_URL}" ] && SEND_ALERTA="NO"
  452. # check flock
  453. [ -z "${FLOCK_WEBHOOK_URL}" ] && SEND_FLOCK="NO"
  454. # check discord
  455. [ -z "${DISCORD_WEBHOOK_URL}" ] && SEND_DISCORD="NO"
  456. # check pushover
  457. [ -z "${PUSHOVER_APP_TOKEN}" ] && SEND_PUSHOVER="NO"
  458. # check pushbullet
  459. [ -z "${PUSHBULLET_ACCESS_TOKEN}" ] && SEND_PUSHBULLET="NO"
  460. # check twilio
  461. { [ -z "${TWILIO_ACCOUNT_TOKEN}" ] || [ -z "${TWILIO_ACCOUNT_SID}" ] || [ -z "${TWILIO_NUMBER}" ]; } && SEND_TWILIO="NO"
  462. # check hipchat
  463. [ -z "${HIPCHAT_AUTH_TOKEN}" ] && SEND_HIPCHAT="NO"
  464. # check messagebird
  465. { [ -z "${MESSAGEBIRD_ACCESS_KEY}" ] || [ -z "${MESSAGEBIRD_NUMBER}" ]; } && SEND_MESSAGEBIRD="NO"
  466. # check kavenegar
  467. { [ -z "${KAVENEGAR_API_KEY}" ] || [ -z "${KAVENEGAR_SENDER}" ]; } && SEND_KAVENEGAR="NO"
  468. # check telegram
  469. [ -z "${TELEGRAM_BOT_TOKEN}" ] && SEND_TELEGRAM="NO"
  470. # check kafka
  471. { [ -z "${KAFKA_URL}" ] || [ -z "${KAFKA_SENDER_IP}" ]; } && SEND_KAFKA="NO"
  472. # check irc
  473. [ -z "${IRC_NETWORK}" ] && SEND_IRC="NO"
  474. # check hangouts
  475. [ ${#HANGOUTS_WEBHOOK_URI[@]} -eq 0 ] && SEND_HANGOUTS="NO"
  476. # check fleep
  477. #shellcheck disable=SC2153
  478. { [ -z "${FLEEP_SERVER}" ] || [ -z "${FLEEP_SENDER}" ]; } && SEND_FLEEP="NO"
  479. # check dynatrace
  480. { [ -z "${DYNATRACE_SPACE}" ] ||
  481. [ -z "${DYNATRACE_SERVER}" ] ||
  482. [ -z "${DYNATRACE_TOKEN}" ] ||
  483. [ -z "${DYNATRACE_TAG_VALUE}" ] ||
  484. [ -z "${DYNATRACE_EVENT}" ]; } && SEND_DYNATRACE="NO"
  485. # check opsgenie
  486. [ -z "${OPSGENIE_API_KEY}" ] && SEND_OPSGENIE="NO"
  487. # check matrix
  488. { [ -z "${MATRIX_HOMESERVER}" ] || [ -z "${MATRIX_ACCESSTOKEN}" ]; } && SEND_MATRIX="NO"
  489. # check stackpulse
  490. [ -z "${STACKPULSE_WEBHOOK}" ] && SEND_STACKPULSE="NO"
  491. # check msteams
  492. [ -z "${MSTEAMS_WEBHOOK_URL}" ] && SEND_MSTEAMS="NO"
  493. # check pd
  494. [ -z "${DEFAULT_RECIPIENT_PD}" ] && SEND_PD="NO"
  495. # check prowl
  496. [ -z "${DEFAULT_RECIPIENT_PROWL}" ] && SEND_PROWL="NO"
  497. # check custom
  498. [ -z "${DEFAULT_RECIPIENT_CUSTOM}" ] && SEND_CUSTOM="NO"
  499. if [ "${SEND_PUSHOVER}" = "YES" ] ||
  500. [ "${SEND_SLACK}" = "YES" ] ||
  501. [ "${SEND_ROCKETCHAT}" = "YES" ] ||
  502. [ "${SEND_ALERTA}" = "YES" ] ||
  503. [ "${SEND_PD}" = "YES" ] ||
  504. [ "${SEND_FLOCK}" = "YES" ] ||
  505. [ "${SEND_DISCORD}" = "YES" ] ||
  506. [ "${SEND_HIPCHAT}" = "YES" ] ||
  507. [ "${SEND_TWILIO}" = "YES" ] ||
  508. [ "${SEND_MESSAGEBIRD}" = "YES" ] ||
  509. [ "${SEND_KAVENEGAR}" = "YES" ] ||
  510. [ "${SEND_TELEGRAM}" = "YES" ] ||
  511. [ "${SEND_PUSHBULLET}" = "YES" ] ||
  512. [ "${SEND_KAFKA}" = "YES" ] ||
  513. [ "${SEND_FLEEP}" = "YES" ] ||
  514. [ "${SEND_PROWL}" = "YES" ] ||
  515. [ "${SEND_HANGOUTS}" = "YES" ] ||
  516. [ "${SEND_MATRIX}" = "YES" ] ||
  517. [ "${SEND_CUSTOM}" = "YES" ] ||
  518. [ "${SEND_MSTEAMS}" = "YES" ] ||
  519. [ "${SEND_DYNATRACE}" = "YES" ] ||
  520. [ "${SEND_STACKPULSE}" = "YES" ] ||
  521. [ "${SEND_OPSGENIE}" = "YES" ]; then
  522. # if we need curl, check for the curl command
  523. if [ -z "${curl}" ]; then
  524. curl="$(command -v curl 2>/dev/null)"
  525. fi
  526. if [ -z "${curl}" ]; then
  527. error "Cannot find curl command in the system path. Disabling all curl based notifications."
  528. SEND_PUSHOVER="NO"
  529. SEND_PUSHBULLET="NO"
  530. SEND_TELEGRAM="NO"
  531. SEND_SLACK="NO"
  532. SEND_MSTEAMS="NO"
  533. SEND_ROCKETCHAT="NO"
  534. SEND_ALERTA="NO"
  535. SEND_PD="NO"
  536. SEND_FLOCK="NO"
  537. SEND_DISCORD="NO"
  538. SEND_TWILIO="NO"
  539. SEND_HIPCHAT="NO"
  540. SEND_MESSAGEBIRD="NO"
  541. SEND_KAVENEGAR="NO"
  542. SEND_KAFKA="NO"
  543. SEND_FLEEP="NO"
  544. SEND_PROWL="NO"
  545. SEND_HANGOUTS="NO"
  546. SEND_MATRIX="NO"
  547. SEND_CUSTOM="NO"
  548. SEND_DYNATRACE="NO"
  549. SEND_STACKPULSE="NO"
  550. SEND_OPSGENIE="NO"
  551. fi
  552. fi
  553. if [ "${SEND_SMS}" = "YES" ]; then
  554. if [ -z "${sendsms}" ]; then
  555. sendsms="$(command -v sendsms 2>/dev/null)"
  556. fi
  557. if [ -z "${sendsms}" ]; then
  558. SEND_SMS="NO"
  559. fi
  560. fi
  561. # if we need sendmail, check for the sendmail command
  562. if [ "${SEND_EMAIL}" = "YES" ] && [ -z "${sendmail}" ]; then
  563. sendmail="$(command -v sendmail 2>/dev/null)"
  564. if [ -z "${sendmail}" ]; then
  565. debug "Cannot find sendmail command in the system path. Disabling email notifications."
  566. SEND_EMAIL="NO"
  567. fi
  568. fi
  569. # if we need logger, check for the logger command
  570. if [ "${SEND_SYSLOG}" = "YES" ] && [ -z "${logger}" ]; then
  571. logger="$(command -v logger 2>/dev/null)"
  572. if [ -z "${logger}" ]; then
  573. debug "Cannot find logger command in the system path. Disabling syslog notifications."
  574. SEND_SYSLOG="NO"
  575. fi
  576. fi
  577. # if we need aws, check for the aws command
  578. if [ "${SEND_AWSSNS}" = "YES" ] && [ -z "${aws}" ]; then
  579. aws="$(command -v aws 2>/dev/null)"
  580. if [ -z "${aws}" ]; then
  581. debug "Cannot find aws command in the system path. Disabling Amazon SNS notifications."
  582. SEND_AWSSNS="NO"
  583. fi
  584. fi
  585. if [ ${dump_methods} ]; then
  586. for name in "${!SEND_@}"; do
  587. if [ "${!name}" = "YES" ]; then
  588. echo "$name"
  589. fi
  590. done
  591. exit
  592. fi
  593. # -----------------------------------------------------------------------------
  594. # find the recipients' addresses per method
  595. # netdata may call us with multiple roles, and roles may have multiple but
  596. # overlapping recipients - so, here we find the unique recipients.
  597. for method_name in ${method_names}; do
  598. send_var="SEND_${method_name^^}"
  599. if [ "${!send_var}" = "NO" ]; then
  600. continue
  601. fi
  602. declare -A arr_var=()
  603. for x in ${roles//,/ }; do
  604. # the roles 'silent' and 'disabled' mean:
  605. # don't send a notification for this role
  606. if [ "${x}" = "silent" ] || [ "${x}" = "disabled" ]; then
  607. continue
  608. fi
  609. role_recipients="role_recipients_${method_name}[$x]"
  610. default_recipient_var="DEFAULT_RECIPIENT_${method_name^^}"
  611. a="${!role_recipients}"
  612. [ -z "${a}" ] && a="${!default_recipient_var}"
  613. for r in ${a//,/ }; do
  614. [ "${r}" != "disabled" ] && filter_recipient_by_criticality ${method_name} "${r}" && arr_var[${r/|*/}]="1"
  615. done
  616. done
  617. # build the list of recipients
  618. to_var="to_${method_name}"
  619. declare to_${method_name}="${!arr_var[*]}"
  620. [ -z "${!to_var}" ] && declare ${send_var}="NO"
  621. done
  622. # -----------------------------------------------------------------------------
  623. # handle fixup of the email recipient list.
  624. fix_to_email() {
  625. to_email=
  626. while [ -n "${1}" ]; do
  627. [ -n "${to_email}" ] && to_email="${to_email}, "
  628. to_email="${to_email}${1}"
  629. shift 1
  630. done
  631. }
  632. # ${to_email} without quotes here
  633. fix_to_email ${to_email}
  634. # -----------------------------------------------------------------------------
  635. # handle output if we're running in unit test mode
  636. if [ ${unittest} ]; then
  637. for method_name in ${method_names}; do
  638. to_var="to_${method_name}"
  639. echo "results: ${method_name}: ${!to_var}"
  640. done
  641. exit 0
  642. fi
  643. # -----------------------------------------------------------------------------
  644. # check that we have at least a method enabled
  645. proceed=0
  646. for method in "${SEND_EMAIL}" \
  647. "${SEND_PUSHOVER}" \
  648. "${SEND_TELEGRAM}" \
  649. "${SEND_SLACK}" \
  650. "${SEND_ROCKETCHAT}" \
  651. "${SEND_ALERTA}" \
  652. "${SEND_FLOCK}" \
  653. "${SEND_DISCORD}" \
  654. "${SEND_TWILIO}" \
  655. "${SEND_HIPCHAT}" \
  656. "${SEND_MESSAGEBIRD}" \
  657. "${SEND_KAVENEGAR}" \
  658. "${SEND_PUSHBULLET}" \
  659. "${SEND_KAFKA}" \
  660. "${SEND_PD}" \
  661. "${SEND_FLEEP}" \
  662. "${SEND_PROWL}" \
  663. "${SEND_MATRIX}" \
  664. "${SEND_CUSTOM}" \
  665. "${SEND_IRC}" \
  666. "${SEND_HANGOUTS}" \
  667. "${SEND_AWSSNS}" \
  668. "${SEND_SYSLOG}" \
  669. "${SEND_SMS}" \
  670. "${SEND_MSTEAMS}" \
  671. "${SEND_DYNATRACE}" \
  672. "${SEND_STACKPULSE}" \
  673. "${SEND_OPSGENIE}" ; do
  674. if [ "${method}" == "YES" ]; then
  675. proceed=1
  676. break
  677. fi
  678. done
  679. if [ "$proceed" -eq 0 ]; then
  680. fatal "All notification methods are disabled. Not sending notification for host '${host}', chart '${chart}' to '${roles}' for '${name}' = '${value}' for status '${status}'."
  681. fi
  682. # -----------------------------------------------------------------------------
  683. # get the date the alarm happened
  684. date=$(date --date=@${when} "${date_format}" 2>/dev/null)
  685. [ -z "${date}" ] && date=$(date "${date_format}" 2>/dev/null)
  686. [ -z "${date}" ] && date=$(date --date=@${when} 2>/dev/null)
  687. [ -z "${date}" ] && date=$(date 2>/dev/null)
  688. # -----------------------------------------------------------------------------
  689. # get the date in utc the alarm happened
  690. date_utc=$(date --date=@${when} "${date_format}" -u 2>/dev/null)
  691. [ -z "${date_utc}" ] && date_utc=$(date -u "${date_format}" 2>/dev/null)
  692. [ -z "${date_utc}" ] && date_utc=$(date -u --date=@${when} 2>/dev/null)
  693. [ -z "${date_utc}" ] && date_utc=$(date -u 2>/dev/null)
  694. # ----------------------------------------------------------------------------
  695. # prepare some extra headers if we've been asked to thread e-mails
  696. if [ "${SEND_EMAIL}" == "YES" ] && [ "${EMAIL_THREADING}" != "NO" ]; then
  697. email_thread_headers="In-Reply-To: <${chart}-${name}@${host}>\\r\\nReferences: <${chart}-${name}@${host}>"
  698. else
  699. email_thread_headers=
  700. fi
  701. # -----------------------------------------------------------------------------
  702. # function to URL encode a string
  703. urlencode() {
  704. local string="${1}" strlen encoded pos c o
  705. strlen=${#string}
  706. for ((pos = 0; pos < strlen; pos++)); do
  707. c=${string:pos:1}
  708. case "${c}" in
  709. [-_.~a-zA-Z0-9])
  710. o="${c}"
  711. ;;
  712. *)
  713. printf -v o '%%%02x' "'${c}"
  714. ;;
  715. esac
  716. encoded+="${o}"
  717. done
  718. REPLY="${encoded}"
  719. echo "${REPLY}"
  720. }
  721. # -----------------------------------------------------------------------------
  722. # function to convert a duration in seconds, to a human readable duration
  723. # using DAYS, MINUTES, SECONDS
  724. duration4human() {
  725. local s="${1}" d=0 h=0 m=0 ds="day" hs="hour" ms="minute" ss="second" ret
  726. d=$((s / 86400))
  727. s=$((s - (d * 86400)))
  728. h=$((s / 3600))
  729. s=$((s - (h * 3600)))
  730. m=$((s / 60))
  731. s=$((s - (m * 60)))
  732. if [ ${d} -gt 0 ]; then
  733. [ ${m} -ge 30 ] && h=$((h + 1))
  734. [ ${d} -gt 1 ] && ds="days"
  735. [ ${h} -gt 1 ] && hs="hours"
  736. if [ ${h} -gt 0 ]; then
  737. ret="${d} ${ds} and ${h} ${hs}"
  738. else
  739. ret="${d} ${ds}"
  740. fi
  741. elif [ ${h} -gt 0 ]; then
  742. [ ${s} -ge 30 ] && m=$((m + 1))
  743. [ ${h} -gt 1 ] && hs="hours"
  744. [ ${m} -gt 1 ] && ms="minutes"
  745. if [ ${m} -gt 0 ]; then
  746. ret="${h} ${hs} and ${m} ${ms}"
  747. else
  748. ret="${h} ${hs}"
  749. fi
  750. elif [ ${m} -gt 0 ]; then
  751. [ ${m} -gt 1 ] && ms="minutes"
  752. [ ${s} -gt 1 ] && ss="seconds"
  753. if [ ${s} -gt 0 ]; then
  754. ret="${m} ${ms} and ${s} ${ss}"
  755. else
  756. ret="${m} ${ms}"
  757. fi
  758. else
  759. [ ${s} -gt 1 ] && ss="seconds"
  760. ret="${s} ${ss}"
  761. fi
  762. REPLY="${ret}"
  763. echo "${REPLY}"
  764. }
  765. # -----------------------------------------------------------------------------
  766. # email sender
  767. send_email() {
  768. local ret opts=() sender_email="${EMAIL_SENDER}" sender_name=
  769. if [ "${SEND_EMAIL}" = "YES" ]; then
  770. if [ -n "${EMAIL_SENDER}" ]; then
  771. if [[ ${EMAIL_SENDER} =~ ^\".*\"\ \<.*\>$ ]]; then
  772. # the name includes double quotes
  773. sender_email="$(echo "${EMAIL_SENDER}" | cut -d '<' -f 2 | cut -d '>' -f 1)"
  774. sender_name="$(echo "${EMAIL_SENDER}" | cut -d '"' -f 2)"
  775. elif [[ ${EMAIL_SENDER} =~ ^\'.*\'\ \<.*\>$ ]]; then
  776. # the name includes single quotes
  777. sender_email="$(echo "${EMAIL_SENDER}" | cut -d '<' -f 2 | cut -d '>' -f 1)"
  778. sender_name="$(echo "${EMAIL_SENDER}" | cut -d "'" -f 2)"
  779. elif [[ ${EMAIL_SENDER} =~ ^.*\ \<.*\>$ ]]; then
  780. # the name does not have any quotes
  781. sender_email="$(echo "${EMAIL_SENDER}" | cut -d '<' -f 2 | cut -d '>' -f 1)"
  782. sender_name="$(echo "${EMAIL_SENDER}" | cut -d '<' -f 1)"
  783. fi
  784. fi
  785. [ -n "${sender_email}" ] && opts+=(-f "${sender_email}")
  786. [ -n "${sender_name}" ] && sendmail --help 2>&1 | grep -q "\-F " && opts+=(-F "${sender_name}")
  787. if [ "${debug}" = "1" ]; then
  788. echo >&2 "--- BEGIN sendmail command ---"
  789. printf >&2 "%q " "${sendmail}" -t "${opts[@]}"
  790. echo >&2
  791. echo >&2 "--- END sendmail command ---"
  792. fi
  793. local cmd_output
  794. cmd_output=$("${sendmail}" -t "${opts[@]}" 2>&1)
  795. ret=$?
  796. if [ ${ret} -eq 0 ]; then
  797. info "sent email notification for: ${host} ${chart}.${name} is ${status} to '${to_email}'"
  798. return 0
  799. else
  800. error "failed to send email notification for: ${host} ${chart}.${name} is ${status} to '${to_email}' with error code ${ret} (${cmd_output})."
  801. return 1
  802. fi
  803. fi
  804. return 1
  805. }
  806. # -----------------------------------------------------------------------------
  807. # pushover sender
  808. send_pushover() {
  809. local apptoken="${1}" usertokens="${2}" when="${3}" url="${4}" status="${5}" title="${6}" message="${7}" httpcode sent=0 user priority
  810. if [ "${SEND_PUSHOVER}" = "YES" ] && [ -n "${apptoken}" ] && [ -n "${usertokens}" ] && [ -n "${title}" ] && [ -n "${message}" ]; then
  811. # https://pushover.net/api
  812. priority=-2
  813. case "${status}" in
  814. CLEAR) priority=-1 ;; # low priority: no sound or vibration
  815. WARNING) priority=0 ;; # normal priority: respect quiet hours
  816. CRITICAL) priority=1 ;; # high priority: bypass quiet hours
  817. *) priority=-2 ;; # lowest priority: no notification at all
  818. esac
  819. for user in ${usertokens}; do
  820. httpcode=$(docurl \
  821. --form-string "token=${apptoken}" \
  822. --form-string "user=${user}" \
  823. --form-string "html=1" \
  824. --form-string "title=${title}" \
  825. --form-string "message=${message}" \
  826. --form-string "timestamp=${when}" \
  827. --form-string "url=${url}" \
  828. --form-string "url_title=Open netdata dashboard to view the alarm" \
  829. --form-string "priority=${priority}" \
  830. https://api.pushover.net/1/messages.json)
  831. if [ "${httpcode}" = "200" ]; then
  832. info "sent pushover notification for: ${host} ${chart}.${name} is ${status} to '${user}'"
  833. sent=$((sent + 1))
  834. else
  835. error "failed to send pushover notification for: ${host} ${chart}.${name} is ${status} to '${user}' with HTTP response status code ${httpcode}."
  836. fi
  837. done
  838. [ ${sent} -gt 0 ] && return 0
  839. fi
  840. return 1
  841. }
  842. # -----------------------------------------------------------------------------
  843. # pushbullet sender
  844. send_pushbullet() {
  845. local userapikey="${1}" source_device="${2}" recipients="${3}" url="${4}" title="${5}" message="${6}" httpcode sent=0 user
  846. if [ "${SEND_PUSHBULLET}" = "YES" ] && [ -n "${userapikey}" ] && [ -n "${recipients}" ] && [ -n "${message}" ] && [ -n "${title}" ]; then
  847. #https://docs.pushbullet.com/#create-push
  848. for user in ${recipients}; do
  849. httpcode=$(docurl \
  850. --header 'Access-Token: '${userapikey}'' \
  851. --header 'Content-Type: application/json' \
  852. --data-binary @<(
  853. cat <<EOF
  854. {"title": "${title}",
  855. "type": "link",
  856. "email": "${user}",
  857. "body": "$(echo -n ${message})",
  858. "url": "${url}",
  859. "source_device_iden": "${source_device}"}
  860. EOF
  861. ) "https://api.pushbullet.com/v2/pushes" -X POST)
  862. if [ "${httpcode}" = "200" ]; then
  863. info "sent pushbullet notification for: ${host} ${chart}.${name} is ${status} to '${user}'"
  864. sent=$((sent + 1))
  865. else
  866. error "failed to send pushbullet notification for: ${host} ${chart}.${name} is ${status} to '${user}' with HTTP response status code ${httpcode}."
  867. fi
  868. done
  869. [ ${sent} -gt 0 ] && return 0
  870. fi
  871. return 1
  872. }
  873. # -----------------------------------------------------------------------------
  874. # kafka sender
  875. send_kafka() {
  876. local httpcode sent=0
  877. if [ "${SEND_KAFKA}" = "YES" ]; then
  878. httpcode=$(docurl -X POST \
  879. --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}\"}" \
  880. "${KAFKA_URL}")
  881. if [ "${httpcode}" = "204" ]; then
  882. info "sent kafka data for: ${host} ${chart}.${name} is ${status} and ip '${KAFKA_SENDER_IP}'"
  883. sent=$((sent + 1))
  884. else
  885. error "failed to send kafka data for: ${host} ${chart}.${name} is ${status} and ip '${KAFKA_SENDER_IP}' with HTTP response status code ${httpcode}."
  886. fi
  887. [ ${sent} -gt 0 ] && return 0
  888. fi
  889. return 1
  890. }
  891. # -----------------------------------------------------------------------------
  892. # pagerduty.com sender
  893. send_pd() {
  894. local recipients="${1}" sent=0 severity current_time payload url response_code
  895. unset t
  896. case ${status} in
  897. CLEAR) t='resolve' ; severity='info' ;;
  898. WARNING) t='trigger' ; severity='warning' ;;
  899. CRITICAL) t='trigger' ; severity='critical' ;;
  900. esac
  901. if [ ${SEND_PD} = "YES" ] && [ -n "${t}" ]; then
  902. if [ "$(uname)" == "Linux" ]; then
  903. current_time=$(date -d @${when} +'%Y-%m-%dT%H:%M:%S.000')
  904. else
  905. current_time=$(date -r ${when} +'%Y-%m-%dT%H:%M:%S.000')
  906. fi
  907. for PD_SERVICE_KEY in ${recipients}; do
  908. d="${status} ${name} = ${value_string} - ${host}, ${family}"
  909. if [ ${USE_PD_VERSION} = "2" ]; then
  910. payload="$(
  911. cat <<EOF
  912. {
  913. "payload" : {
  914. "summary": "${info:0:1024}",
  915. "source" : "${args_host}",
  916. "severity" : "${severity}",
  917. "timestamp" : "${current_time}",
  918. "group" : "${family}",
  919. "class" : "${chart}",
  920. "custom_details": {
  921. "value_w_units": "${value_string}",
  922. "when": "${when}",
  923. "duration" : "${duration}",
  924. "roles": "${roles}",
  925. "alarm_id" : "${alarm_id}",
  926. "name" : "${name}",
  927. "chart" : "${chart}",
  928. "family" : "${family}",
  929. "status" : "${status}",
  930. "old_status" : "${old_status}",
  931. "value" : "${value}",
  932. "old_value" : "${old_value}",
  933. "src" : "${src}",
  934. "non_clear_duration" : "${non_clear_duration}",
  935. "units" : "${units}",
  936. "info" : "${info}"
  937. }
  938. },
  939. "routing_key": "${PD_SERVICE_KEY}",
  940. "event_action": "${t}",
  941. "dedup_key": "${unique_id}"
  942. }
  943. EOF
  944. )"
  945. url="https://events.pagerduty.com/v2/enqueue"
  946. response_code="202"
  947. else
  948. payload="$(
  949. cat <<EOF
  950. {
  951. "service_key": "${PD_SERVICE_KEY}",
  952. "event_type": "${t}",
  953. "incident_key" : "${alarm_id}",
  954. "description": "${d}",
  955. "details": {
  956. "value_w_units": "${value_string}",
  957. "when": "${when}",
  958. "duration" : "${duration}",
  959. "roles": "${roles}",
  960. "alarm_id" : "${alarm_id}",
  961. "name" : "${name}",
  962. "chart" : "${chart}",
  963. "family" : "${family}",
  964. "status" : "${status}",
  965. "old_status" : "${old_status}",
  966. "value" : "${value}",
  967. "old_value" : "${old_value}",
  968. "src" : "${src}",
  969. "non_clear_duration" : "${non_clear_duration}",
  970. "units" : "${units}",
  971. "info" : "${info}"
  972. }
  973. }
  974. EOF
  975. )"
  976. url="https://events.pagerduty.com/generic/2010-04-15/create_event.json"
  977. response_code="200"
  978. fi
  979. httpcode=$(docurl -X POST --data "${payload}" ${url})
  980. if [ "${httpcode}" = "${response_code}" ]; then
  981. info "sent pagerduty notification for: ${host} ${chart}.${name} is ${status}'"
  982. sent=$((sent + 1))
  983. else
  984. error "failed to send pagerduty notification for: ${host} ${chart}.${name} is ${status}, with HTTP response status code ${httpcode}."
  985. fi
  986. done
  987. [ ${sent} -gt 0 ] && return 0
  988. fi
  989. return 1
  990. }
  991. # -----------------------------------------------------------------------------
  992. # twilio sender
  993. send_twilio() {
  994. local accountsid="${1}" accounttoken="${2}" twilionumber="${3}" recipients="${4}" title="${5}" message="${6}" httpcode sent=0 user
  995. if [ "${SEND_TWILIO}" = "YES" ] && [ -n "${accountsid}" ] && [ -n "${accounttoken}" ] && [ -n "${twilionumber}" ] && [ -n "${recipients}" ] && [ -n "${message}" ] && [ -n "${title}" ]; then
  996. #https://www.twilio.com/packages/labs/code/bash/twilio-sms
  997. for user in ${recipients}; do
  998. httpcode=$(docurl -X POST \
  999. --data-urlencode "From=${twilionumber}" \
  1000. --data-urlencode "To=${user}" \
  1001. --data-urlencode "Body=${title} ${message}" \
  1002. -u "${accountsid}:${accounttoken}" \
  1003. "https://api.twilio.com/2010-04-01/Accounts/${accountsid}/Messages.json")
  1004. if [ "${httpcode}" = "201" ]; then
  1005. info "sent Twilio SMS for: ${host} ${chart}.${name} is ${status} to '${user}'"
  1006. sent=$((sent + 1))
  1007. else
  1008. error "failed to send Twilio SMS for: ${host} ${chart}.${name} is ${status} to '${user}' with HTTP response status code ${httpcode}."
  1009. fi
  1010. done
  1011. [ ${sent} -gt 0 ] && return 0
  1012. fi
  1013. return 1
  1014. }
  1015. # -----------------------------------------------------------------------------
  1016. # hipchat sender
  1017. send_hipchat() {
  1018. local authtoken="${1}" recipients="${2}" message="${3}" httpcode sent=0 room color msg_format notify
  1019. # remove <small></small> from the message
  1020. message="${message//<small>/}"
  1021. message="${message//<\/small>/}"
  1022. if [ "${SEND_HIPCHAT}" = "YES" ] && [ -n "${HIPCHAT_SERVER}" ] && [ -n "${authtoken}" ] && [ -n "${recipients}" ] && [ -n "${message}" ]; then
  1023. # Valid values: html, text.
  1024. # Defaults to 'html'.
  1025. msg_format="html"
  1026. # Background color for message. Valid values: yellow, green, red, purple, gray, random. Defaults to 'yellow'.
  1027. case "${status}" in
  1028. WARNING) color="yellow" ;;
  1029. CRITICAL) color="red" ;;
  1030. CLEAR) color="green" ;;
  1031. *) color="gray" ;;
  1032. esac
  1033. # Whether this message should trigger a user notification (change the tab color, play a sound, notify mobile phones, etc).
  1034. # Each recipient's notification preferences are taken into account.
  1035. # Defaults to false.
  1036. notify="true"
  1037. for room in ${recipients}; do
  1038. httpcode=$(docurl -X POST \
  1039. -H "Content-type: application/json" \
  1040. -H "Authorization: Bearer ${authtoken}" \
  1041. -d "{\"color\": \"${color}\", \"from\": \"${host}\", \"message_format\": \"${msg_format}\", \"message\": \"${message}\", \"notify\": \"${notify}\"}" \
  1042. "https://${HIPCHAT_SERVER}/v2/room/${room}/notification")
  1043. if [ "${httpcode}" = "204" ]; then
  1044. info "sent HipChat notification for: ${host} ${chart}.${name} is ${status} to '${room}'"
  1045. sent=$((sent + 1))
  1046. else
  1047. error "failed to send HipChat notification for: ${host} ${chart}.${name} is ${status} to '${room}' with HTTP response status code ${httpcode}."
  1048. fi
  1049. done
  1050. [ ${sent} -gt 0 ] && return 0
  1051. fi
  1052. return 1
  1053. }
  1054. # -----------------------------------------------------------------------------
  1055. # messagebird sender
  1056. send_messagebird() {
  1057. local accesskey="${1}" messagebirdnumber="${2}" recipients="${3}" title="${4}" message="${5}" httpcode sent=0 user
  1058. if [ "${SEND_MESSAGEBIRD}" = "YES" ] && [ -n "${accesskey}" ] && [ -n "${messagebirdnumber}" ] && [ -n "${recipients}" ] && [ -n "${message}" ] && [ -n "${title}" ]; then
  1059. #https://developers.messagebird.com/docs/messaging
  1060. for user in ${recipients}; do
  1061. httpcode=$(docurl -X POST \
  1062. --data-urlencode "originator=${messagebirdnumber}" \
  1063. --data-urlencode "recipients=${user}" \
  1064. --data-urlencode "body=${title} ${message}" \
  1065. --data-urlencode "datacoding=auto" \
  1066. -H "Authorization: AccessKey ${accesskey}" \
  1067. "https://rest.messagebird.com/messages")
  1068. if [ "${httpcode}" = "201" ]; then
  1069. info "sent Messagebird SMS for: ${host} ${chart}.${name} is ${status} to '${user}'"
  1070. sent=$((sent + 1))
  1071. else
  1072. error "failed to send Messagebird SMS for: ${host} ${chart}.${name} is ${status} to '${user}' with HTTP response status code ${httpcode}."
  1073. fi
  1074. done
  1075. [ ${sent} -gt 0 ] && return 0
  1076. fi
  1077. return 1
  1078. }
  1079. # -----------------------------------------------------------------------------
  1080. # kavenegar sender
  1081. send_kavenegar() {
  1082. local API_KEY="${1}" kavenegarsender="${2}" recipients="${3}" title="${4}" message="${5}" httpcode sent=0 user
  1083. if [ "${SEND_KAVENEGAR}" = "YES" ] && [ -n "${API_KEY}" ] && [ -n "${kavenegarsender}" ] && [ -n "${recipients}" ] && [ -n "${message}" ] && [ -n "${title}" ]; then
  1084. # http://api.kavenegar.com/v1/{API-KEY}/sms/send.json
  1085. for user in ${recipients}; do
  1086. httpcode=$(docurl -X POST http://api.kavenegar.com/v1/${API_KEY}/sms/send.json \
  1087. --data-urlencode "sender=${kavenegarsender}" \
  1088. --data-urlencode "receptor=${user}" \
  1089. --data-urlencode "message=${title} ${message}")
  1090. if [ "${httpcode}" = "200" ]; then
  1091. info "sent Kavenegar SMS for: ${host} ${chart}.${name} is ${status} to '${user}'"
  1092. sent=$((sent + 1))
  1093. else
  1094. error "failed to send Kavenegar SMS for: ${host} ${chart}.${name} is ${status} to '${user}' with HTTP response status code ${httpcode}."
  1095. fi
  1096. done
  1097. [ ${sent} -gt 0 ] && return 0
  1098. fi
  1099. return 1
  1100. }
  1101. # -----------------------------------------------------------------------------
  1102. # telegram sender
  1103. send_telegram() {
  1104. local bottoken="${1}" chatids="${2}" message="${3}" httpcode sent=0 chatid emoji disableNotification=""
  1105. if [ "${status}" = "CLEAR" ]; then disableNotification="--data-urlencode disable_notification=true"; fi
  1106. case "${status}" in
  1107. WARNING) emoji="⚠️" ;;
  1108. CRITICAL) emoji="🔴" ;;
  1109. CLEAR) emoji="✅" ;;
  1110. *) emoji="⚪️" ;;
  1111. esac
  1112. if [ "${SEND_TELEGRAM}" = "YES" ] && [ -n "${bottoken}" ] && [ -n "${chatids}" ] && [ -n "${message}" ]; then
  1113. for chatid in ${chatids}; do
  1114. # https://core.telegram.org/bots/api#sendmessage
  1115. httpcode=$(docurl ${disableNotification} \
  1116. --data-urlencode "parse_mode=HTML" \
  1117. --data-urlencode "disable_web_page_preview=true" \
  1118. --data-urlencode "text=${emoji} ${message}" \
  1119. "https://api.telegram.org/bot${bottoken}/sendMessage?chat_id=${chatid}")
  1120. if [ "${httpcode}" = "200" ]; then
  1121. info "sent telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}'"
  1122. sent=$((sent + 1))
  1123. elif [ "${httpcode}" = "401" ]; then
  1124. error "failed to send telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}': Wrong bot token."
  1125. else
  1126. error "failed to send telegram notification for: ${host} ${chart}.${name} is ${status} to '${chatid}' with HTTP response status code ${httpcode}."
  1127. fi
  1128. done
  1129. [ ${sent} -gt 0 ] && return 0
  1130. fi
  1131. return 1
  1132. }
  1133. # -----------------------------------------------------------------------------
  1134. # Microsoft Team sender
  1135. send_msteams() {
  1136. local webhook="${1}" channels="${2}" httpcode sent=0 channel color payload
  1137. [ "${SEND_MSTEAMS}" != "YES" ] && return 1
  1138. case "${status}" in
  1139. WARNING) icon="${MSTEAMS_ICON_WARNING}" && color="${MSTEAMS_COLOR_WARNING}" ;;
  1140. CRITICAL) icon="${MSTEAMS_ICON_CRITICAL}" && color="${MSTEAMS_COLOR_CRITICAL}" ;;
  1141. CLEAR) icon="${MSTEAMS_ICON_CLEAR}" && color="${MSTEAMS_COLOR_CLEAR}" ;;
  1142. *) icon="${MSTEAMS_ICON_DEFAULT}" && color="${MSTEAMS_COLOR_DEFAULT}" ;;
  1143. esac
  1144. for channel in ${channels}; do
  1145. ## More details are available here regarding the payload syntax options : https://docs.microsoft.com/en-us/outlook/actionable-messages/message-card-reference
  1146. ## Online designer : https://adaptivecards.io/designer/
  1147. payload="$(
  1148. cat <<EOF
  1149. {
  1150. "@context": "http://schema.org/extensions",
  1151. "@type": "MessageCard",
  1152. "themeColor": "${color}",
  1153. "title": "$icon Alert ${status} from netdata for ${host}",
  1154. "text": "${host} ${status_message}, ${chart} (_${family}_), *${alarm}*",
  1155. "potentialAction": [
  1156. {
  1157. "@type": "OpenUri",
  1158. "name": "Netdata",
  1159. "targets": [
  1160. { "os": "default", "uri": "${goto_url}" }
  1161. ]
  1162. }
  1163. ]
  1164. }
  1165. EOF
  1166. )"
  1167. # Replacing in the webhook CHANNEL string by the MS Teams channel name from conf file.
  1168. webhook="${webhook//CHANNEL/${channel}}"
  1169. httpcode=$(docurl -H "Content-Type: application/json" -d "${payload}" "${webhook}")
  1170. if [ "${httpcode}" = "200" ]; then
  1171. info "sent Microsoft team notification for: ${host} ${chart}.${name} is ${status} to '${webhook}'"
  1172. sent=$((sent + 1))
  1173. else
  1174. error "failed to send Microsoft team notification for: ${host} ${chart}.${name} is ${status} to '${webhook}', with HTTP response status code ${httpcode}."
  1175. fi
  1176. done
  1177. [ ${sent} -gt 0 ] && return 0
  1178. return 1
  1179. }
  1180. # slack sender
  1181. send_slack() {
  1182. local webhook="${1}" channels="${2}" httpcode sent=0 channel color payload
  1183. [ "${SEND_SLACK}" != "YES" ] && return 1
  1184. case "${status}" in
  1185. WARNING) color="warning" ;;
  1186. CRITICAL) color="danger" ;;
  1187. CLEAR) color="good" ;;
  1188. *) color="#777777" ;;
  1189. esac
  1190. for channel in ${channels}; do
  1191. # Default entry in the recipient is without a hash in front (backwards-compatible). Accept specification of channel or user.
  1192. if [ "${channel::1}" != "#" ] && [ "${channel::1}" != "@" ]; then channel="#$channel"; fi
  1193. # If channel is equal to "#" then do not send the channel attribute at all. Slack also defines channels and users in webhooks.
  1194. if [ "${channel}" = "#" ]; then
  1195. ch=""
  1196. chstr="without specifying a channel"
  1197. else
  1198. ch="\"channel\": \"${channel}\","
  1199. chstr="to '${channel}'"
  1200. fi
  1201. payload="$(
  1202. cat <<EOF
  1203. {
  1204. $ch
  1205. "username": "netdata on ${host}",
  1206. "icon_url": "${images_base_url}/images/banner-icon-144x144.png",
  1207. "text": "${host} ${status_message}, \`${chart}\` (_${family}_), *${alarm}*",
  1208. "attachments": [
  1209. {
  1210. "fallback": "${alarm} - ${chart} (${family}) - ${info}",
  1211. "color": "${color}",
  1212. "title": "${alarm}",
  1213. "title_link": "${goto_url}",
  1214. "text": "${info}",
  1215. "fields": [
  1216. {
  1217. "title": "${chart}",
  1218. "short": true
  1219. },
  1220. {
  1221. "title": "${family}",
  1222. "short": true
  1223. }
  1224. ],
  1225. "thumb_url": "${image}",
  1226. "footer": "by ${host}",
  1227. "ts": ${when}
  1228. }
  1229. ]
  1230. }
  1231. EOF
  1232. )"
  1233. httpcode=$(docurl -X POST --data-urlencode "payload=${payload}" "${webhook}")
  1234. if [ "${httpcode}" = "200" ]; then
  1235. info "sent slack notification for: ${host} ${chart}.${name} is ${status} ${chstr}"
  1236. sent=$((sent + 1))
  1237. else
  1238. error "failed to send slack notification for: ${host} ${chart}.${name} is ${status} ${chstr}, with HTTP response status code ${httpcode}."
  1239. fi
  1240. done
  1241. [ ${sent} -gt 0 ] && return 0
  1242. return 1
  1243. }
  1244. # -----------------------------------------------------------------------------
  1245. # rocketchat sender
  1246. send_rocketchat() {
  1247. local webhook="${1}" channels="${2}" httpcode sent=0 channel color payload
  1248. [ "${SEND_ROCKETCHAT}" != "YES" ] && return 1
  1249. case "${status}" in
  1250. WARNING) color="warning" ;;
  1251. CRITICAL) color="danger" ;;
  1252. CLEAR) color="good" ;;
  1253. *) color="#777777" ;;
  1254. esac
  1255. for channel in ${channels}; do
  1256. payload="$(
  1257. cat <<EOF
  1258. {
  1259. "channel": "#${channel}",
  1260. "alias": "netdata on ${host}",
  1261. "avatar": "${images_base_url}/images/banner-icon-144x144.png",
  1262. "text": "${host} ${status_message}, \`${chart}\` (_${family}_), *${alarm}*",
  1263. "attachments": [
  1264. {
  1265. "color": "${color}",
  1266. "title": "${alarm}",
  1267. "title_link": "${goto_url}",
  1268. "text": "${info}",
  1269. "fields": [
  1270. {
  1271. "title": "${chart}",
  1272. "short": true,
  1273. "value": "chart"
  1274. },
  1275. {
  1276. "title": "${family}",
  1277. "short": true,
  1278. "value": "family"
  1279. }
  1280. ],
  1281. "thumb_url": "${image}",
  1282. "ts": "${when}"
  1283. }
  1284. ]
  1285. }
  1286. EOF
  1287. )"
  1288. httpcode=$(docurl -X POST --data-urlencode "payload=${payload}" "${webhook}")
  1289. if [ "${httpcode}" = "200" ]; then
  1290. info "sent rocketchat notification for: ${host} ${chart}.${name} is ${status} to '${channel}'"
  1291. sent=$((sent + 1))
  1292. else
  1293. error "failed to send rocketchat notification for: ${host} ${chart}.${name} is ${status} to '${channel}', with HTTP response status code ${httpcode}."
  1294. fi
  1295. done
  1296. [ ${sent} -gt 0 ] && return 0
  1297. return 1
  1298. }
  1299. # -----------------------------------------------------------------------------
  1300. # alerta sender
  1301. send_alerta() {
  1302. local webhook="${1}" channels="${2}" httpcode sent=0 channel severity resource event payload auth
  1303. [ "${SEND_ALERTA}" != "YES" ] && return 1
  1304. case "${status}" in
  1305. CRITICAL) severity="critical" ;;
  1306. WARNING) severity="warning" ;;
  1307. CLEAR) severity="cleared" ;;
  1308. *) severity="indeterminate" ;;
  1309. esac
  1310. if [[ ${chart} == httpcheck* ]]; then
  1311. resource=$chart
  1312. event=$name
  1313. else
  1314. resource="${host}:${family}"
  1315. event="${chart}.${name}"
  1316. fi
  1317. for channel in ${channels}; do
  1318. payload="$(
  1319. cat <<EOF
  1320. {
  1321. "resource": "${resource}",
  1322. "event": "${event}",
  1323. "environment": "${channel}",
  1324. "severity": "${severity}",
  1325. "service": ["Netdata"],
  1326. "group": "Performance",
  1327. "value": "${value_string}",
  1328. "text": "${info}",
  1329. "tags": ["alarm_id:${alarm_id}"],
  1330. "attributes": {
  1331. "roles": "${roles}",
  1332. "name": "${name}",
  1333. "chart": "${chart}",
  1334. "family": "${family}",
  1335. "source": "${src}",
  1336. "moreInfo": "<a href=\"${goto_url}\">View Netdata</a>"
  1337. },
  1338. "origin": "netdata/${host}",
  1339. "type": "netdataAlarm",
  1340. "rawData": "${BASH_ARGV[@]}"
  1341. }
  1342. EOF
  1343. )"
  1344. if [ -n "${ALERTA_API_KEY}" ]; then
  1345. auth="Key ${ALERTA_API_KEY}"
  1346. fi
  1347. httpcode=$(docurl -X POST "${webhook}/alert" -H "Content-Type: application/json" -H "Authorization: $auth" --data "${payload}")
  1348. if [ "${httpcode}" = "200" ] || [ "${httpcode}" = "201" ]; then
  1349. info "sent alerta notification for: ${host} ${chart}.${name} is ${status} to '${channel}'"
  1350. sent=$((sent + 1))
  1351. elif [ "${httpcode}" = "202" ]; then
  1352. info "suppressed alerta notification for: ${host} ${chart}.${name} is ${status} to '${channel}'"
  1353. else
  1354. error "failed to send alerta notification for: ${host} ${chart}.${name} is ${status} to '${channel}', with HTTP response status code ${httpcode}."
  1355. fi
  1356. done
  1357. [ ${sent} -gt 0 ] && return 0
  1358. return 1
  1359. }
  1360. # -----------------------------------------------------------------------------
  1361. # flock sender
  1362. send_flock() {
  1363. local webhook="${1}" channels="${2}" httpcode sent=0 channel color payload
  1364. [ "${SEND_FLOCK}" != "YES" ] && return 1
  1365. case "${status}" in
  1366. WARNING) color="warning" ;;
  1367. CRITICAL) color="danger" ;;
  1368. CLEAR) color="good" ;;
  1369. *) color="#777777" ;;
  1370. esac
  1371. for channel in ${channels}; do
  1372. httpcode=$(docurl -X POST "${webhook}" -H "Content-Type: application/json" -d "{
  1373. \"sendAs\": {
  1374. \"name\" : \"netdata on ${host}\",
  1375. \"profileImage\" : \"${images_base_url}/images/banner-icon-144x144.png\"
  1376. },
  1377. \"text\": \"${host} *${status_message}*\",
  1378. \"timestamp\": \"${when}\",
  1379. \"attachments\": [
  1380. {
  1381. \"description\": \"${chart} (${family}) - ${info}\",
  1382. \"color\": \"${color}\",
  1383. \"title\": \"${alarm}\",
  1384. \"url\": \"${goto_url}\",
  1385. \"text\": \"${info}\",
  1386. \"views\": {
  1387. \"image\": {
  1388. \"original\": { \"src\": \"${image}\", \"width\": 400, \"height\": 400 },
  1389. \"thumbnail\": { \"src\": \"${image}\", \"width\": 50, \"height\": 50 },
  1390. \"filename\": \"${image}\"
  1391. }
  1392. }
  1393. }
  1394. ]
  1395. }")
  1396. if [ "${httpcode}" = "200" ]; then
  1397. info "sent flock notification for: ${host} ${chart}.${name} is ${status} to '${channel}'"
  1398. sent=$((sent + 1))
  1399. else
  1400. error "failed to send flock notification for: ${host} ${chart}.${name} is ${status} to '${channel}', with HTTP response status code ${httpcode}."
  1401. fi
  1402. done
  1403. [ ${sent} -gt 0 ] && return 0
  1404. return 1
  1405. }
  1406. # -----------------------------------------------------------------------------
  1407. # discord sender
  1408. send_discord() {
  1409. local webhook="${1}/slack" channels="${2}" httpcode sent=0 channel color payload username
  1410. [ "${SEND_DISCORD}" != "YES" ] && return 1
  1411. case "${status}" in
  1412. WARNING) color="warning" ;;
  1413. CRITICAL) color="danger" ;;
  1414. CLEAR) color="good" ;;
  1415. *) color="#777777" ;;
  1416. esac
  1417. for channel in ${channels}; do
  1418. username="netdata on ${host}"
  1419. [ ${#username} -gt 32 ] && username="${username:0:29}..."
  1420. payload="$(
  1421. cat <<EOF
  1422. {
  1423. "channel": "#${channel}",
  1424. "username": "${username}",
  1425. "text": "${host} ${status_message}, \`${chart}\` (_${family}_), *${alarm}*",
  1426. "icon_url": "${images_base_url}/images/banner-icon-144x144.png",
  1427. "attachments": [
  1428. {
  1429. "color": "${color}",
  1430. "title": "${alarm}",
  1431. "title_link": "${goto_url}",
  1432. "text": "${info}",
  1433. "fields": [
  1434. {
  1435. "title": "${chart}",
  1436. "value": "${family}"
  1437. }
  1438. ],
  1439. "thumb_url": "${image}",
  1440. "footer_icon": "${images_base_url}/images/banner-icon-144x144.png",
  1441. "footer": "${host}",
  1442. "ts": ${when}
  1443. }
  1444. ]
  1445. }
  1446. EOF
  1447. )"
  1448. httpcode=$(docurl -X POST --data-urlencode "payload=${payload}" "${webhook}")
  1449. if [ "${httpcode}" = "200" ]; then
  1450. info "sent discord notification for: ${host} ${chart}.${name} is ${status} to '${channel}'"
  1451. sent=$((sent + 1))
  1452. else
  1453. error "failed to send discord notification for: ${host} ${chart}.${name} is ${status} to '${channel}', with HTTP response status code ${httpcode}."
  1454. fi
  1455. done
  1456. [ ${sent} -gt 0 ] && return 0
  1457. return 1
  1458. }
  1459. # -----------------------------------------------------------------------------
  1460. # fleep sender
  1461. send_fleep() {
  1462. local httpcode sent=0 webhooks="${1}" data message
  1463. if [ "${SEND_FLEEP}" = "YES" ]; then
  1464. message="${host} ${status_message}, \`${chart}\` (${family}), *${alarm}*\\n${info}"
  1465. for hook in ${webhooks}; do
  1466. data="{ "
  1467. data="${data} 'message': '${message}', "
  1468. data="${data} 'user': '${FLEEP_SENDER}' "
  1469. data="${data} }"
  1470. httpcode=$(docurl -X POST --data "${data}" "https://fleep.io/hook/${hook}")
  1471. if [ "${httpcode}" = "200" ]; then
  1472. info "sent fleep data for: ${host} ${chart}.${name} is ${status} and user '${FLEEP_SENDER}'"
  1473. sent=$((sent + 1))
  1474. else
  1475. error "failed to send fleep data for: ${host} ${chart}.${name} is ${status} and user '${FLEEP_SENDER}' with HTTP response status code ${httpcode}."
  1476. fi
  1477. done
  1478. [ ${sent} -gt 0 ] && return 0
  1479. fi
  1480. return 1
  1481. }
  1482. # -----------------------------------------------------------------------------
  1483. # Prowl sender
  1484. send_prowl() {
  1485. local httpcode sent=0 data message keys prio=0 alarm_url event
  1486. if [ "${SEND_PROWL}" = "YES" ]; then
  1487. message="$(urlencode "${host} ${status_message}, \`${chart}\` (${family}), *${alarm}*\\n${info}")"
  1488. message="description=${message}"
  1489. keys="$(urlencode "$(echo "${1}" | tr ' ' ,)")"
  1490. keys="apikey=${keys}"
  1491. app="application=Netdata"
  1492. case "${status}" in
  1493. CRITICAL)
  1494. prio=2
  1495. ;;
  1496. WARNING)
  1497. prio=1
  1498. ;;
  1499. esac
  1500. prio="priority=${prio}"
  1501. alarm_url="$(urlencode ${goto_url})"
  1502. alarm_url="url=${alarm_url}"
  1503. event="$(urlencode "${host} ${status_message}")"
  1504. event="event=${event}"
  1505. data="${keys}&${prio}&${alarm_url}&${app}&${event}&${message}"
  1506. httpcode=$(docurl -X POST --data "${data}" "https://api.prowlapp.com/publicapi/add")
  1507. if [ "${httpcode}" = "200" ]; then
  1508. info "sent prowl data for: ${host} ${chart}.${name} is ${status}"
  1509. sent=1
  1510. else
  1511. error "failed to send prowl data for: ${host} ${chart}.${name} is ${status} with with error code ${httpcode}."
  1512. fi
  1513. [ ${sent} -gt 0 ] && return 0
  1514. fi
  1515. return 1
  1516. }
  1517. # -----------------------------------------------------------------------------
  1518. # irc sender
  1519. send_irc() {
  1520. local NICKNAME="${1}" REALNAME="${2}" CHANNELS="${3}" NETWORK="${4}" PORT="${5}" SERVERNAME="${6}" MESSAGE="${7}" sent=0 channel color send_alarm reply_codes error
  1521. if [ "${SEND_IRC}" = "YES" ] && [ -n "${NICKNAME}" ] && [ -n "${REALNAME}" ] && [ -n "${CHANNELS}" ] && [ -n "${NETWORK}" ] && [ -n "${SERVERNAME}" ] && [ -n "${PORT}" ]; then
  1522. case "${status}" in
  1523. WARNING) color="warning" ;;
  1524. CRITICAL) color="danger" ;;
  1525. CLEAR) color="good" ;;
  1526. *) color="#777777" ;;
  1527. esac
  1528. SNDMESSAGE="${MESSAGE//$'\n'/", "}"
  1529. for CHANNEL in ${CHANNELS}; do
  1530. error=0
  1531. send_alarm=$(echo -e "USER ${NICKNAME} guest ${REALNAME} ${SERVERNAME}\\nNICK ${NICKNAME}\\nJOIN ${CHANNEL}\\nPRIVMSG ${CHANNEL} :${SNDMESSAGE}\\nQUIT\\n" \ | nc "${NETWORK}" "${PORT}")
  1532. reply_codes=$(echo "${send_alarm}" | cut -d ' ' -f 2 | grep -o '[0-9]*')
  1533. for code in ${reply_codes}; do
  1534. if [ "${code}" -ge 400 ] && [ "${code}" -le 599 ]; then
  1535. error=1
  1536. break
  1537. fi
  1538. done
  1539. if [ "${error}" -eq 0 ]; then
  1540. info "sent irc notification for: ${host} ${chart}.${name} is ${status} to '${CHANNEL}'"
  1541. sent=$((sent + 1))
  1542. else
  1543. error "failed to send irc notification for: ${host} ${chart}.${name} is ${status} to '${CHANNEL}', with error code ${code}."
  1544. fi
  1545. done
  1546. fi
  1547. [ ${sent} -gt 0 ] && return 0
  1548. return 1
  1549. }
  1550. # -----------------------------------------------------------------------------
  1551. # Amazon SNS sender
  1552. send_awssns() {
  1553. local targets="${1}" message='' sent=0 region=''
  1554. local default_format="${status} on ${host} at ${date}: ${chart} ${value_string}"
  1555. [ "${SEND_AWSSNS}" = "YES" ] || return 1
  1556. message=${AWSSNS_MESSAGE_FORMAT:-${default_format}}
  1557. for target in ${targets}; do
  1558. # Extract the region from the target ARN. We need to explicitly specify the region so that it matches up correctly.
  1559. region="$(echo ${target} | cut -f 4 -d ':')"
  1560. if ${aws} sns publish --region "${region}" --subject "${host} ${status_message} - ${name//_/ } - ${chart}" --message "${message}" --target-arn ${target} &>/dev/null; then
  1561. info "sent Amazon SNS notification for: ${host} ${chart}.${name} is ${status} to '${target}'"
  1562. sent=$((sent + 1))
  1563. else
  1564. error "failed to send Amazon SNS notification for: ${host} ${chart}.${name} is ${status} to '${target}'"
  1565. fi
  1566. done
  1567. [ ${sent} -gt 0 ] && return 0
  1568. return 1
  1569. }
  1570. # -----------------------------------------------------------------------------
  1571. # Matrix sender
  1572. send_matrix() {
  1573. local homeserver="${1}" webhook accesstoken rooms="${2}" httpcode sent=0 payload
  1574. [ "${SEND_MATRIX}" != "YES" ] && return 1
  1575. [ -z "${MATRIX_ACCESSTOKEN}" ] && return 1
  1576. accesstoken="${MATRIX_ACCESSTOKEN}"
  1577. case "${status}" in
  1578. WARNING) emoji="⚠️" ;;
  1579. CRITICAL) emoji="🔴" ;;
  1580. CLEAR) emoji="✅" ;;
  1581. *) emoji="⚪️" ;;
  1582. esac
  1583. for room in ${rooms}; do
  1584. webhook="$homeserver/_matrix/client/r0/rooms/$(urlencode $room)/send/m.room.message?access_token=$accesstoken"
  1585. payload="$(
  1586. cat <<EOF
  1587. {
  1588. "msgtype": "m.notice",
  1589. "format": "org.matrix.custom.html",
  1590. "formatted_body": "${emoji} ${host} ${status_message} - <b>${name//_/ }</b><br>${chart} (${family})<br><a href=\"${goto_url}\">${alarm}</a><br><i>${info}</i>",
  1591. "body": "${emoji} ${host} ${status_message} - ${name//_/ } ${chart} (${family}) ${goto_url} ${alarm} ${info}"
  1592. }
  1593. EOF
  1594. )"
  1595. httpcode=$(docurl -X POST --data "${payload}" "${webhook}")
  1596. if [ "${httpcode}" == "200" ]; then
  1597. info "sent Matrix notification for: ${host} ${chart}.${name} is ${status} to '${room}'"
  1598. sent=$((sent + 1))
  1599. else
  1600. error "failed to send Matrix notification for: ${host} ${chart}.${name} is ${status} to '${room}', with HTTP response status code ${httpcode}."
  1601. fi
  1602. done
  1603. [ ${sent} -gt 0 ] && return 0
  1604. return 1
  1605. }
  1606. # -----------------------------------------------------------------------------
  1607. # syslog sender
  1608. send_syslog() {
  1609. local facility=${SYSLOG_FACILITY:-"local6"} level='info' targets="${1}"
  1610. local priority='' message='' server='' port='' prefix=''
  1611. local temp1='' temp2=''
  1612. [ "${SEND_SYSLOG}" = "YES" ] || return 1
  1613. if [ "${status}" = "CRITICAL" ]; then
  1614. level='crit'
  1615. elif [ "${status}" = "WARNING" ]; then
  1616. level='warning'
  1617. fi
  1618. for target in ${targets}; do
  1619. priority="${facility}.${level}"
  1620. message=''
  1621. server=''
  1622. port=''
  1623. prefix=''
  1624. temp1=''
  1625. temp2=''
  1626. prefix=$(echo ${target} | cut -d '/' -f 2)
  1627. temp1=$(echo ${target} | cut -d '/' -f 1)
  1628. if [ ${prefix} != ${temp1} ]; then
  1629. if (echo ${temp1} | grep -q '@'); then
  1630. temp2=$(echo ${temp1} | cut -d '@' -f 1)
  1631. server=$(echo ${temp1} | cut -d '@' -f 2)
  1632. if [ ${temp2} != ${server} ]; then
  1633. priority=${temp2}
  1634. fi
  1635. port=$(echo ${server} | rev | cut -d ':' -f 1 | rev)
  1636. if (echo ${server} | grep -E -q '\[.*\]'); then
  1637. if (echo ${port} | grep -q ']'); then
  1638. port=''
  1639. else
  1640. server=$(echo ${server} | rev | cut -d ':' -f 2- | rev)
  1641. fi
  1642. else
  1643. if [ ${port} = ${server} ]; then
  1644. port=''
  1645. else
  1646. server=$(echo ${server} | cut -d ':' -f 1)
  1647. fi
  1648. fi
  1649. else
  1650. priority=${temp1}
  1651. fi
  1652. fi
  1653. message="${prefix} ${status} on ${host} at ${date}: ${chart} ${value_string}"
  1654. if [ ${server} ]; then
  1655. logger_options="${logger_options} -n ${server}"
  1656. if [ ${port} ]; then
  1657. logger_options="${logger_options} -P ${port}"
  1658. fi
  1659. fi
  1660. ${logger} -p ${priority} ${logger_options} "${message}"
  1661. done
  1662. return $?
  1663. }
  1664. # -----------------------------------------------------------------------------
  1665. # SMS sender
  1666. send_sms() {
  1667. local recipients="${1}" errcode errmessage sent=0
  1668. # Human readable SMS
  1669. local msg="${host} ${status_message}: ${chart} (${family}), ${alarm}"
  1670. # limit it to 160 characters
  1671. msg="${msg:0:160}"
  1672. if [ "${SEND_SMS}" = "YES" ] && [ -n "${sendsms}" ] && [ -n "${recipients}" ] && [ -n "${msg}" ]; then
  1673. # http://api.kavenegar.com/v1/{API-KEY}/sms/send.json
  1674. for phone in ${recipients}; do
  1675. errmessage=$($sendsms $phone "$msg" 2>&1)
  1676. errcode=$?
  1677. if [ ${errcode} -eq 0 ]; then
  1678. info "sent smstools3 SMS for: ${host} ${chart}.${name} is ${status} to '${user}'"
  1679. sent=$((sent + 1))
  1680. else
  1681. error "failed to send smstools3 SMS for: ${host} ${chart}.${name} is ${status} to '${user}' with error code ${errcode}: ${errmessage}."
  1682. fi
  1683. done
  1684. [ ${sent} -gt 0 ] && return 0
  1685. fi
  1686. return 1
  1687. }
  1688. # -----------------------------------------------------------------------------
  1689. # hangouts sender
  1690. send_hangouts() {
  1691. local rooms="${1}" httpcode sent=0 room color payload webhook thread
  1692. [ "${SEND_HANGOUTS}" != "YES" ] && return 1
  1693. case "${status}" in
  1694. WARNING) color="#ffa700" ;;
  1695. CRITICAL) color="#d62d20" ;;
  1696. CLEAR) color="#008744" ;;
  1697. *) color="#777777" ;;
  1698. esac
  1699. for room in ${rooms}; do
  1700. if [ -z "${HANGOUTS_WEBHOOK_URI[$room]}" ] ; then
  1701. info "Can't send Hangouts notification for: ${host} ${chart}.${name} to room ${room}. HANGOUTS_WEBHOOK_URI[$room] not defined"
  1702. else
  1703. if [ -n "${HANGOUTS_WEBHOOK_THREAD[$room]}" ]; then
  1704. thread="\"name\" : \"${HANGOUTS_WEBHOOK_THREAD[$room]}\""
  1705. fi
  1706. webhook="${HANGOUTS_WEBHOOK_URI[$room]}"
  1707. payload="$(
  1708. cat <<EOF
  1709. {
  1710. "cards": [
  1711. {
  1712. "header": {
  1713. "title": "Netdata on ${host}",
  1714. "imageUrl": "${images_base_url}/images/banner-icon-144x144.png",
  1715. "imageStyle": "IMAGE"
  1716. },
  1717. "sections": [
  1718. {
  1719. "header": "<b>${host}</b>",
  1720. "widgets": [
  1721. {
  1722. "keyValue": {
  1723. "topLabel": "Status Message",
  1724. "content": "<b>${status_message}</b>",
  1725. "contentMultiline": "true",
  1726. "iconUrl": "${image}",
  1727. "onClick": {
  1728. "openLink": {
  1729. "url": "${goto_url}"
  1730. }
  1731. }
  1732. }
  1733. },
  1734. {
  1735. "keyValue": {
  1736. "topLabel": "${chart} | ${family}",
  1737. "content": "<font color=${color}>${alarm}</font>",
  1738. "contentMultiline": "true"
  1739. }
  1740. }
  1741. ]
  1742. },
  1743. {
  1744. "widgets": [
  1745. {
  1746. "textParagraph": {
  1747. "text": "<font color=\"#0057e7\">@ ${date}\n<b>${info}</b></font>"
  1748. }
  1749. }
  1750. ]
  1751. },
  1752. {
  1753. "widgets": [
  1754. {
  1755. "buttons": [
  1756. {
  1757. "textButton": {
  1758. "text": "Go to ${host}",
  1759. "onClick": {
  1760. "openLink": {
  1761. "url": "${goto_url}"
  1762. }
  1763. }
  1764. }
  1765. }
  1766. ]
  1767. }
  1768. ]
  1769. }
  1770. ]
  1771. }
  1772. ],
  1773. "thread": {
  1774. $thread
  1775. }
  1776. }
  1777. EOF
  1778. )"
  1779. httpcode=$(docurl -H "Content-Type: application/json" -X POST -d "${payload}" "${webhook}")
  1780. if [ "${httpcode}" = "200" ]; then
  1781. info "sent hangouts notification for: ${host} ${chart}.${name} is ${status} to '${room}'"
  1782. sent=$((sent + 1))
  1783. else
  1784. error "failed to send hangouts notification for: ${host} ${chart}.${name} is ${status} to '${room}', with HTTP response status code ${httpcode}."
  1785. fi
  1786. fi
  1787. done
  1788. [ ${sent} -gt 0 ] && return 0
  1789. return 1
  1790. }
  1791. # -----------------------------------------------------------------------------
  1792. # Dynatrace sender
  1793. send_dynatrace() {
  1794. [ "${SEND_DYNATRACE}" != "YES" ] && return 1
  1795. local dynatrace_url="${DYNATRACE_SERVER}/e/${DYNATRACE_SPACE}/api/v1/events"
  1796. local description="NetData Notification for: ${host} ${chart}.${name} is ${status}"
  1797. local payload=""
  1798. payload=$(cat <<EOF
  1799. {
  1800. "title": "NetData Alarm from ${host}",
  1801. "source" : "${DYNATRACE_ANNOTATION_TYPE}",
  1802. "description" : "${description}",
  1803. "eventType": "${DYNATRACE_EVENT}",
  1804. "attachRules":{
  1805. "tagRule":[{
  1806. "meTypes":["HOST"],
  1807. "tags":["${DYNATRACE_TAG_VALUE}"]
  1808. }]
  1809. },
  1810. "customProperties":{
  1811. "description": "${description}"
  1812. }
  1813. }
  1814. EOF
  1815. )
  1816. # echo ${payload}
  1817. httpcode=$(docurl -X POST -H "Authorization: Api-token ${DYNATRACE_TOKEN}" -H "Content-Type: application/json" -d "${payload}" ${dynatrace_url})
  1818. ret=$?
  1819. if [ ${ret} -eq 0 ]; then
  1820. if [ "${httpcode}" = "200" ]; then
  1821. info "sent ${DYNATRACE_EVENT} to ${DYNATRACE_SERVER}"
  1822. return 0
  1823. else
  1824. warning "Dynatrace ${DYNATRACE_SERVER} responded ${httpcode} notification for: ${host} ${chart}.${name} is ${status} was not sent!"
  1825. return 1
  1826. fi
  1827. else
  1828. error "failed to sent ${DYNATRACE_EVENT} notification for: ${host} ${chart}.${name} is ${status} to ${DYNATRACE_SERVER} with error code ${ret}."
  1829. return 1
  1830. fi
  1831. }
  1832. # -----------------------------------------------------------------------------
  1833. # Stackpulse sender
  1834. send_stackpulse() {
  1835. local payload httpcode oldv currv
  1836. [ "${SEND_STACKPULSE}" != "YES" ] && return 1
  1837. # We are sending null when values are nan to avoid errors while JSON message is parsed
  1838. [ "${old_value}" != "nan" ] && oldv="${old_value}" || oldv="null"
  1839. [ "${value}" != "nan" ] && currv="${value}" || currv="null"
  1840. payload=$(cat <<EOF
  1841. {
  1842. "Node" : "${host}",
  1843. "Chart" : "${chart}",
  1844. "OldValue" : ${oldv},
  1845. "Value" : ${currv},
  1846. "Units" : "${units}",
  1847. "OldStatus" : "${old_status}",
  1848. "Status" : "${status}",
  1849. "Alarm" : "${name}",
  1850. "Date": ${when},
  1851. "Duration": ${duration},
  1852. "NonClearDuration": ${non_clear_duration},
  1853. "Description" : "${status_message}, ${info}",
  1854. "CalcExpression" : "${calc_expression}",
  1855. "CalcParamValues" : "${calc_param_values}",
  1856. "TotalWarnings" : "${total_warnings}",
  1857. "TotalCritical" : "${total_critical}",
  1858. "ID" : ${alarm_id}
  1859. }
  1860. EOF
  1861. )
  1862. httpcode=$(docurl -X POST -H "Content-Type: application/json" -d "${payload}" ${STACKPULSE_WEBHOOK})
  1863. if [ "${httpcode}" = "200" ]; then
  1864. info "sent stackpulse notification for: ${host} ${chart}.${name} is ${status}"
  1865. else
  1866. error "failed to send stackpulse notification for: ${host} ${chart}.${name} is ${status}, with HTTP response status code ${httpcode}."
  1867. return 1
  1868. fi
  1869. return 0
  1870. }
  1871. # -----------------------------------------------------------------------------
  1872. # Opsgenie sender
  1873. send_opsgenie() {
  1874. local payload httpcode oldv currv
  1875. [ "${SEND_OPSGENIE}" != "YES" ] && return 1
  1876. if [ -z "${OPSGENIE_API_KEY}" ] ; then
  1877. info "Can't send Opsgenie notification, because OPSGENIE_API_KEY is not defined"
  1878. return 1
  1879. fi
  1880. # We are sending null when values are nan to avoid errors while JSON message is parsed
  1881. [ "${old_value}" != "nan" ] && oldv="${old_value}" || oldv="null"
  1882. [ "${value}" != "nan" ] && currv="${value}" || currv="null"
  1883. payload=$(cat <<EOF
  1884. {
  1885. "host" : "${host}",
  1886. "unique_id" : "${unique_id}",
  1887. "alarmId" : ${alarm_id},
  1888. "eventId" : ${event_id},
  1889. "chart" : "${chart}",
  1890. "when": ${when},
  1891. "name" : "${name}",
  1892. "family" : "${family}",
  1893. "status" : "${status}",
  1894. "old_status" : "${old_status}",
  1895. "value" : ${currv},
  1896. "old_value" : ${oldv},
  1897. "duration": ${duration},
  1898. "non_clear_duration": ${non_clear_duration},
  1899. "units" : "${units}",
  1900. "info" : "${status_message}, ${info}",
  1901. "calc_expression" : "${calc_expression}",
  1902. "total_warnings" : "${total_warnings}",
  1903. "total_critical" : "${total_critical}",
  1904. "src" : "${src}"
  1905. }
  1906. EOF
  1907. )
  1908. httpcode=$(docurl -X POST -H "Content-Type: application/json" -d "${payload}" "${OPSGENIE_API_URL}/v1/json/integrations/webhooks/netdata?apiKey=${OPSGENIE_API_KEY}")
  1909. # https://docs.opsgenie.com/docs/alert-api#create-alert
  1910. if [ "${httpcode}" = "200" ]; then
  1911. info "sent opsgenie notification for: ${host} ${chart}.${name} is ${status}"
  1912. else
  1913. error "failed to send opsgenie notification for: ${host} ${chart}.${name} is ${status}, with HTTP error code ${httpcode}."
  1914. return 1
  1915. fi
  1916. return 0
  1917. }
  1918. # -----------------------------------------------------------------------------
  1919. # prepare the content of the notification
  1920. # the url to send the user on click
  1921. urlencode "${args_host}" >/dev/null
  1922. url_host="${REPLY}"
  1923. urlencode "${chart}" >/dev/null
  1924. url_chart="${REPLY}"
  1925. urlencode "${family}" >/dev/null
  1926. url_family="${REPLY}"
  1927. urlencode "${name}" >/dev/null
  1928. url_name="${REPLY}"
  1929. urlencode "${value_string}" >/dev/null
  1930. url_value_string="${REPLY}"
  1931. redirect_params="host=${url_host}&chart=${url_chart}&family=${url_family}&alarm=${url_name}&alarm_unique_id=${unique_id}&alarm_id=${alarm_id}&alarm_event_id=${event_id}&alarm_when=${when}&alarm_status=${status}&alarm_chart=${chart}&alarm_value=${url_value_string}"
  1932. GOTOCLOUD=0
  1933. if [ "${NETDATA_REGISTRY_URL}" == "https://registry.my-netdata.io" ]; then
  1934. if [ -z "${NETDATA_REGISTRY_UNIQUE_ID}" ]; then
  1935. if [ -f "@registrydir_POST@/netdata.public.unique.id" ]; then
  1936. NETDATA_REGISTRY_UNIQUE_ID="$(cat "@registrydir_POST@/netdata.public.unique.id")"
  1937. fi
  1938. fi
  1939. if [ -n "${NETDATA_REGISTRY_UNIQUE_ID}" ]; then
  1940. GOTOCLOUD=1
  1941. fi
  1942. fi
  1943. if [ ${GOTOCLOUD} -eq 0 ]; then
  1944. goto_url="${NETDATA_REGISTRY_URL}/goto-host-from-alarm.html?${redirect_params}"
  1945. else
  1946. # Temporarily disable alarm redirection, as the cloud endpoint no longer exists. This functionality will be restored after discussion on #9487. For now, just lead to netdata.cloud
  1947. # Re-allow alarm redirection, for alarms 2.0, new template
  1948. goto_url="${NETDATA_REGISTRY_CLOUD_BASE_URL}/alarms/redirect?agentId=${NETDATA_REGISTRY_UNIQUE_ID}&${redirect_params}"
  1949. fi
  1950. # the severity of the alarm
  1951. severity="${status}"
  1952. # the time the alarm was raised
  1953. duration4human ${duration} >/dev/null
  1954. duration_txt="${REPLY}"
  1955. duration4human ${non_clear_duration} >/dev/null
  1956. non_clear_duration_txt="${REPLY}"
  1957. raised_for="(was ${old_status,,} for ${duration_txt})"
  1958. # the key status message
  1959. status_message="status unknown"
  1960. # the color of the alarm
  1961. color="grey"
  1962. # the alarm value
  1963. alarm="${name//_/ } = ${value_string}"
  1964. # the image of the alarm
  1965. image="${images_base_url}/images/banner-icon-144x144.png"
  1966. # have a default email status, in case the following case does not catch it
  1967. status_email_subject="${status}"
  1968. # prepare the title based on status
  1969. case "${status}" in
  1970. CRITICAL)
  1971. image="${images_base_url}/images/alert-128-red.png"
  1972. alarm_badge="${NETDATA_REGISTRY_CLOUD_BASE_URL}/static/email/img/label_critical.png"
  1973. status_message="is critical"
  1974. status_email_subject="Critical"
  1975. color="#ca414b"
  1976. rich_status_raised_for="Raised to critical, for ${non_clear_duration_txt}"
  1977. background_color="#FFEBEF"
  1978. border_color="#FF4136"
  1979. text_color="#FF4136"
  1980. action_text_color="#FFFFFF"
  1981. ;;
  1982. WARNING)
  1983. image="${images_base_url}/images/alert-128-orange.png"
  1984. alarm_badge="${NETDATA_REGISTRY_CLOUD_BASE_URL}/static/email/img/label_warning.png"
  1985. status_message="needs attention"
  1986. status_email_subject="Warning"
  1987. color="#ffc107"
  1988. rich_status_raised_for="Raised to warning, for ${non_clear_duration_txt}"
  1989. background_color="#FFF8E1"
  1990. border_color="#FFC300"
  1991. text_color="#536775"
  1992. action_text_color="#35414A"
  1993. ;;
  1994. CLEAR)
  1995. image="${images_base_url}/images/check-mark-2-128-green.png"
  1996. alarm_badge="${NETDATA_REGISTRY_CLOUD_BASE_URL}/static/email/img/label_recovered.png"
  1997. status_message="recovered"
  1998. status_email_subject="Clear"
  1999. color="#77ca6d"
  2000. rich_status_raised_for=
  2001. background_color="#E5F5E8"
  2002. border_color="#68C47D"
  2003. text_color="#00AB44"
  2004. action_text_color="#FFFFFF"
  2005. ;;
  2006. esac
  2007. # the html email subject
  2008. html_email_subject="${status_email_subject}, ${name} = ${value_string}, on ${host}"
  2009. if [ "${status}" = "CLEAR" ]; then
  2010. severity="Recovered from ${old_status}"
  2011. if [ ${non_clear_duration} -gt ${duration} ]; then
  2012. raised_for="(alarm was raised for ${non_clear_duration_txt})"
  2013. fi
  2014. rich_status_raised_for="Recovered from ${old_status,,}, ${raised_for}"
  2015. # don't show the value when the status is CLEAR
  2016. # for certain alarms, this value might not have any meaning
  2017. alarm="${name//_/ } ${raised_for}"
  2018. html_email_subject="${status_email_subject}, ${name} ${raised_for}, on ${host}"
  2019. elif { [ "${old_status}" = "WARNING" ] && [ "${status}" = "CRITICAL" ]; }; then
  2020. severity="Escalated to ${status}"
  2021. if [ ${non_clear_duration} -gt ${duration} ]; then
  2022. raised_for="(alarm is raised for ${non_clear_duration_txt})"
  2023. fi
  2024. rich_status_raised_for="Escalated to critical, ${raised_for}"
  2025. elif { [ "${old_status}" = "CRITICAL" ] && [ "${status}" = "WARNING" ]; }; then
  2026. severity="Demoted to ${status}"
  2027. if [ ${non_clear_duration} -gt ${duration} ]; then
  2028. raised_for="(alarm is raised for ${non_clear_duration_txt})"
  2029. fi
  2030. rich_status_raised_for="Demoted to warning, ${raised_for}"
  2031. else
  2032. raised_for=
  2033. fi
  2034. # prepare HTML versions of elements
  2035. info_html=
  2036. [ -n "${info}" ] && info_html=" <small><br/>${info}</small>"
  2037. raised_for_html=
  2038. [ -n "${raised_for}" ] && raised_for_html="<br/><small>${raised_for}</small>"
  2039. # -----------------------------------------------------------------------------
  2040. # send the slack notification
  2041. # slack aggregates posts from the same username
  2042. # so we use "${host} ${status}" as the bot username, to make them diff
  2043. send_slack "${SLACK_WEBHOOK_URL}" "${to_slack}"
  2044. SENT_SLACK=$?
  2045. # -----------------------------------------------------------------------------
  2046. # send the hangouts notification
  2047. # hangouts aggregates posts from the same room
  2048. # so we use "${host} ${status}" as the room, to make them diff
  2049. send_hangouts "${to_hangouts}"
  2050. SENT_HANGOUTS=$?
  2051. # -----------------------------------------------------------------------------
  2052. # send the Microsoft Teams notification
  2053. # Microsoft teams aggregates posts from the same username
  2054. # so we use "${host} ${status}" as the bot username, to make them diff
  2055. send_msteams "${MSTEAMS_WEBHOOK_URL}" "${to_msteams}"
  2056. SENT_MSTEAMS=$?
  2057. # -----------------------------------------------------------------------------
  2058. # send the rocketchat notification
  2059. # rocketchat aggregates posts from the same username
  2060. # so we use "${host} ${status}" as the bot username, to make them diff
  2061. send_rocketchat "${ROCKETCHAT_WEBHOOK_URL}" "${to_rocketchat}"
  2062. SENT_ROCKETCHAT=$?
  2063. # -----------------------------------------------------------------------------
  2064. # send the alerta notification
  2065. # alerta aggregates posts from the same username
  2066. # so we use "${host} ${status}" as the bot username, to make them diff
  2067. send_alerta "${ALERTA_WEBHOOK_URL}" "${to_alerta}"
  2068. SENT_ALERTA=$?
  2069. # -----------------------------------------------------------------------------
  2070. # send the flock notification
  2071. # flock aggregates posts from the same username
  2072. # so we use "${host} ${status}" as the bot username, to make them diff
  2073. send_flock "${FLOCK_WEBHOOK_URL}" "${to_flock}"
  2074. SENT_FLOCK=$?
  2075. # -----------------------------------------------------------------------------
  2076. # send the discord notification
  2077. # discord aggregates posts from the same username
  2078. # so we use "${host} ${status}" as the bot username, to make them diff
  2079. send_discord "${DISCORD_WEBHOOK_URL}" "${to_discord}"
  2080. SENT_DISCORD=$?
  2081. # -----------------------------------------------------------------------------
  2082. # send the pushover notification
  2083. send_pushover "${PUSHOVER_APP_TOKEN}" "${to_pushover}" "${when}" "${goto_url}" "${status}" "${host} ${status_message} - ${name//_/ } - ${chart}" "
  2084. <font color=\"${color}\"><b>${alarm}</b></font>${info_html}<br/>&nbsp;
  2085. <small><b>${chart}</b><br/>Chart<br/>&nbsp;</small>
  2086. <small><b>${family}</b><br/>Family<br/>&nbsp;</small>
  2087. <small><b>${severity}</b><br/>Severity<br/>&nbsp;</small>
  2088. <small><b>${date}${raised_for_html}</b><br/>Time<br/>&nbsp;</small>
  2089. <a href=\"${goto_url}\">View Netdata</a><br/>&nbsp;
  2090. <small><small>The source of this alarm is line ${src}</small></small>
  2091. "
  2092. SENT_PUSHOVER=$?
  2093. # -----------------------------------------------------------------------------
  2094. # send the pushbullet notification
  2095. send_pushbullet "${PUSHBULLET_ACCESS_TOKEN}" "${PUSHBULLET_SOURCE_DEVICE}" "${to_pushbullet}" "${goto_url}" "${host} ${status_message} - ${name//_/ } - ${chart}" "${alarm}\\n
  2096. Severity: ${severity}\\n
  2097. Chart: ${chart}\\n
  2098. Family: ${family}\\n
  2099. ${date}\\n
  2100. The source of this alarm is line ${src}"
  2101. SENT_PUSHBULLET=$?
  2102. # -----------------------------------------------------------------------------
  2103. # send the twilio SMS
  2104. send_twilio "${TWILIO_ACCOUNT_SID}" "${TWILIO_ACCOUNT_TOKEN}" "${TWILIO_NUMBER}" "${to_twilio}" "${host} ${status_message} - ${name//_/ } - ${chart}" "${alarm}
  2105. Severity: ${severity}
  2106. Chart: ${chart}
  2107. Family: ${family}
  2108. ${info}"
  2109. SENT_TWILIO=$?
  2110. # -----------------------------------------------------------------------------
  2111. # send the messagebird SMS
  2112. send_messagebird "${MESSAGEBIRD_ACCESS_KEY}" "${MESSAGEBIRD_NUMBER}" "${to_messagebird}" "${host} ${status_message} - ${name//_/ } - ${chart}" "${alarm}
  2113. Severity: ${severity}
  2114. Chart: ${chart}
  2115. Family: ${family}
  2116. ${info}"
  2117. SENT_MESSAGEBIRD=$?
  2118. # -----------------------------------------------------------------------------
  2119. # send the kavenegar SMS
  2120. send_kavenegar "${KAVENEGAR_API_KEY}" "${KAVENEGAR_SENDER}" "${to_kavenegar}" "${host} ${status_message} - ${name//_/ } - ${chart}" "${alarm}
  2121. Severity: ${severity}
  2122. Chart: ${chart}
  2123. Family: ${family}
  2124. ${info}"
  2125. SENT_KAVENEGAR=$?
  2126. # -----------------------------------------------------------------------------
  2127. # send the telegram.org message
  2128. # https://core.telegram.org/bots/api#formatting-options
  2129. send_telegram "${TELEGRAM_BOT_TOKEN}" "${to_telegram}" "${host} ${status_message} - <b>${name//_/ }</b>
  2130. ${chart} (${family})
  2131. <a href=\"${goto_url}\">${alarm}</a>
  2132. <i>${info}</i>"
  2133. SENT_TELEGRAM=$?
  2134. # -----------------------------------------------------------------------------
  2135. # send the kafka message
  2136. send_kafka
  2137. SENT_KAFKA=$?
  2138. # -----------------------------------------------------------------------------
  2139. # send the pagerduty.com message
  2140. send_pd "${to_pd}"
  2141. SENT_PD=$?
  2142. # -----------------------------------------------------------------------------
  2143. # send the fleep message
  2144. send_fleep "${to_fleep}"
  2145. SENT_FLEEP=$?
  2146. # -----------------------------------------------------------------------------
  2147. # send the Prowl message
  2148. send_prowl "${to_prowl}"
  2149. SENT_PROWL=$?
  2150. # -----------------------------------------------------------------------------
  2151. # send the irc message
  2152. send_irc "${IRC_NICKNAME}" "${IRC_REALNAME}" "${to_irc}" "${IRC_NETWORK}" "${IRC_PORT}" "${host}" "${host} ${status_message} - ${name//_/ } - ${chart} ----- ${alarm}
  2153. Severity: ${severity}
  2154. Chart: ${chart}
  2155. Family: ${family}
  2156. ${info}"
  2157. SENT_IRC=$?
  2158. # -----------------------------------------------------------------------------
  2159. # send the SMS message with smstools3
  2160. send_sms "${to_sms}"
  2161. SENT_SMS=$?
  2162. # -----------------------------------------------------------------------------
  2163. # send the custom message
  2164. send_custom() {
  2165. # is it enabled?
  2166. [ "${SEND_CUSTOM}" != "YES" ] && return 1
  2167. # do we have any sender?
  2168. [ -z "${1}" ] && return 1
  2169. # call the custom_sender function
  2170. custom_sender "${@}"
  2171. }
  2172. send_custom "${to_custom}"
  2173. SENT_CUSTOM=$?
  2174. # -----------------------------------------------------------------------------
  2175. # send hipchat message
  2176. send_hipchat "${HIPCHAT_AUTH_TOKEN}" "${to_hipchat}" " \
  2177. ${host} ${status_message}<br/> \
  2178. <b>${alarm}</b> ${info_html}<br/> \
  2179. <b>${chart}</b> (family <b>${family}</b>)<br/> \
  2180. <b>${date}${raised_for_html}</b><br/> \
  2181. <a href=\\\"${goto_url}\\\">View netdata dashboard</a> \
  2182. (source of alarm ${src}) \
  2183. "
  2184. SENT_HIPCHAT=$?
  2185. # -----------------------------------------------------------------------------
  2186. # send the Amazon SNS message
  2187. send_awssns "${to_awssns}"
  2188. SENT_AWSSNS=$?
  2189. # -----------------------------------------------------------------------------
  2190. # send the Matrix message
  2191. send_matrix "${MATRIX_HOMESERVER}" "${to_matrix}"
  2192. SENT_MATRIX=$?
  2193. # -----------------------------------------------------------------------------
  2194. # send the syslog message
  2195. send_syslog "${to_syslog}"
  2196. SENT_SYSLOG=$?
  2197. # -----------------------------------------------------------------------------
  2198. # send the email
  2199. IFS='' read -r -d '' email_plaintext_part <<EOF
  2200. Content-Type: text/plain; encoding=${EMAIL_CHARSET}
  2201. Content-Disposition: inline
  2202. Content-Transfer-Encoding: 8bit
  2203. ${host} ${status_message}
  2204. ${alarm} ${info}
  2205. ${raised_for}
  2206. Chart : ${chart}
  2207. Family : ${family}
  2208. Severity: ${severity}
  2209. URL : ${goto_url}
  2210. Source : ${src}
  2211. Date : ${date}
  2212. Notification generated on ${host}
  2213. Evaluated Expression : ${calc_expression}
  2214. Expression Variables : ${calc_param_values}
  2215. The host has ${total_warnings} WARNING and ${total_critical} CRITICAL alarm(s) raised.
  2216. EOF
  2217. if [[ "${EMAIL_PLAINTEXT_ONLY}" == "YES" ]]; then
  2218. send_email <<EOF
  2219. To: ${to_email}
  2220. Subject: ${host} ${status_message} - ${name//_/ } - ${chart}
  2221. MIME-Version: 1.0
  2222. Content-Type: multipart/alternative; boundary="multipart-boundary"
  2223. ${email_thread_headers}
  2224. This is a MIME-encoded multipart message
  2225. --multipart-boundary
  2226. ${email_plaintext_part}
  2227. --multipart-boundary--
  2228. EOF
  2229. else
  2230. now=$(date "+%s")
  2231. if [ -n "$total_warn_alarms" ]; then
  2232. while read -d, -r pair; do
  2233. IFS='=' read -r key val <<<"$pair"
  2234. date_w=$(date --date=@${val} "${date_format}" 2>/dev/null)
  2235. [ -z "${date_w}" ] && date_w=$(date "${date_format}" 2>/dev/null)
  2236. [ -z "${date_w}" ] && date_w=$(date --date=@${val} 2>/dev/null)
  2237. [ -z "${date_w}" ] && date_w=$(date 2>/dev/null)
  2238. elapsed=$((now - val))
  2239. duration4human ${elapsed} >/dev/null
  2240. elapsed_txt="${REPLY}"
  2241. WARN_ALARMS+="
  2242. <div class=\"set-font\" style=\"font-family: 'IBM Plex Sans', sans-serif; background: #FFFFFF; background-color: #FFFFFF; margin: 0px auto; max-width: 600px;\">
  2243. <table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"background:#FFFFFF;background-color:#FFFFFF;width:100%;\">
  2244. <tbody>
  2245. <tr>
  2246. <td style=\"border-top:8px solid #F7F8F8;direction:ltr;font-size:0px;padding:20px 0;text-align:center;\">
  2247. <!--[if mso | IE]><table role=\"presentation\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr><td class=\"\" style=\"vertical-align:top;width:300px;\" ><![endif]-->
  2248. <div class=\"mj-column-per-50 mj-outlook-group-fix\" style=\"font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:50%;\">
  2249. <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"vertical-align:top;\" width=\"100%\">
  2250. <tbody>
  2251. <tr>
  2252. <td align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\">
  2253. <div style=\"font-family:Open Sans, sans-serif;font-size:14px;font-weight:600;line-height:1;text-align:left;color:#35414A;\">${key}</div>
  2254. </td>
  2255. </tr>
  2256. <tr>
  2257. <td align=\"left\" style=\"font-size:0px;padding:10px 25px;padding-top:2px;word-break:break-word;\">
  2258. <div style=\"font-family:Open Sans, sans-serif;font-size:12px;line-height:1;text-align:left;color:#35414A;\">${date_w}</div>
  2259. </td>
  2260. </tr>
  2261. </tbody>
  2262. </table>
  2263. </div>
  2264. <!--[if mso | IE]></td><td class=\"\" style=\"vertical-align:top;width:300px;\" ><![endif]-->
  2265. <div class=\"mj-column-per-50 mj-outlook-group-fix\" style=\"font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:50%;\">
  2266. <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" width=\"100%\">
  2267. <tbody>
  2268. <tr>
  2269. <td style=\"vertical-align:top;padding-top:13px;\">
  2270. <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style width=\"100%\">
  2271. <tbody>
  2272. <tr>
  2273. <td align=\"right\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\">
  2274. <div style=\"font-family:Open Sans, sans-serif;font-size:13px;line-height:1;text-align:right;color:#555555;\"><span style=\"background-color:#FFF8E1; border: 1px solid #FFC300; border-radius:36px; padding: 2px 12px; margin-top: 20px; white-space: nowrap\">
  2275. Warning for ${elapsed_txt}
  2276. </span></div>
  2277. </td>
  2278. </tr>
  2279. </tbody>
  2280. </table>
  2281. </td>
  2282. </tr>
  2283. </tbody>
  2284. </table>
  2285. </div>
  2286. <!--[if mso | IE]></td></tr></table><![endif]-->
  2287. </td>
  2288. </tr>
  2289. </tbody>
  2290. </table>
  2291. </div>
  2292. "
  2293. done <<<"$total_warn_alarms,"
  2294. fi
  2295. if [ -n "$total_crit_alarms" ]; then
  2296. while read -d, -r pair; do
  2297. IFS='=' read -r key val <<<"$pair"
  2298. date_c=$(date --date=@${val} "${date_format}" 2>/dev/null)
  2299. [ -z "${date_c}" ] && date_c=$(date "${date_format}" 2>/dev/null)
  2300. [ -z "${date_c}" ] && date_c=$(date --date=@${val} 2>/dev/null)
  2301. [ -z "${date_c}" ] && date_c=$(date 2>/dev/null)
  2302. elapsed=$((now - val))
  2303. duration4human ${elapsed} >/dev/null
  2304. elapsed_txt="${REPLY}"
  2305. CRIT_ALARMS+="
  2306. <div class=\"set-font\" style=\"font-family: 'IBM Plex Sans', sans-serif; background: #FFFFFF; background-color: #FFFFFF; margin: 0px auto; max-width: 600px;\">
  2307. <table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"background:#FFFFFF;background-color:#FFFFFF;width:100%;\">
  2308. <tbody>
  2309. <tr>
  2310. <td style=\"border-top:8px solid #F7F8F8;direction:ltr;font-size:0px;padding:20px 0;text-align:center;\">
  2311. <!--[if mso | IE]><table role=\"presentation\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr><td class=\"\" style=\"vertical-align:top;width:300px;\" ><![endif]-->
  2312. <div class=\"mj-column-per-50 mj-outlook-group-fix\" style=\"font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:50%;\">
  2313. <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"vertical-align:top;\" width=\"100%\">
  2314. <tbody>
  2315. <tr>
  2316. <td align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\">
  2317. <div style=\"font-family:Open Sans, sans-serif;font-size:14px;font-weight:600;line-height:1;text-align:left;color:#35414A;\">${key}</div>
  2318. </td>
  2319. </tr>
  2320. <tr>
  2321. <td align=\"left\" style=\"font-size:0px;padding:10px 25px;padding-top:2px;word-break:break-word;\">
  2322. <div style=\"font-family:Open Sans, sans-serif;font-size:12px;line-height:1;text-align:left;color:#35414A;\">${date_c}</div>
  2323. </td>
  2324. </tr>
  2325. </tbody>
  2326. </table>
  2327. </div>
  2328. <!--[if mso | IE]></td><td class=\"\" style=\"vertical-align:top;width:300px;\" ><![endif]-->
  2329. <div class=\"mj-column-per-50 mj-outlook-group-fix\" style=\"font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:50%;\">
  2330. <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" width=\"100%\">
  2331. <tbody>
  2332. <tr>
  2333. <td style=\"vertical-align:top;padding-top:13px;\">
  2334. <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style width=\"100%\">
  2335. <tbody>
  2336. <tr>
  2337. <td align=\"right\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\">
  2338. <div style=\"font-family:Open Sans, sans-serif;font-size:13px;line-height:1;text-align:right;color:#35414A;\"><span style=\"background-color:#FFEBEF; border: 1px solid #FF4136; border-radius:36px; padding: 2px 12px; margin-top: 20px; white-space: nowrap\">
  2339. Critical for ${elapsed_txt}
  2340. </span></div>
  2341. </td>
  2342. </tr>
  2343. </tbody>
  2344. </table>
  2345. </td>
  2346. </tr>
  2347. </tbody>
  2348. </table>
  2349. </div>
  2350. <!--[if mso | IE]></td></tr></table><![endif]-->
  2351. </td>
  2352. </tr>
  2353. </tbody>
  2354. </table>
  2355. </div>
  2356. "
  2357. done <<<"$total_crit_alarms,"
  2358. fi
  2359. if [ -n "$edit_command_line" ]; then
  2360. IFS='=' read -r edit_command line <<<"$edit_command_line"
  2361. fi
  2362. IFS='' read -r -d '' email_html_part <<EOF
  2363. Content-Type: text/html; encoding=${EMAIL_CHARSET}
  2364. Content-Disposition: inline
  2365. Content-Transfer-Encoding: 8bit
  2366. <!doctype html>
  2367. <html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
  2368. <head>
  2369. <title>
  2370. </title>
  2371. <!--[if !mso]><!-->
  2372. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  2373. <!--<![endif]-->
  2374. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  2375. <meta name="viewport" content="width=device-width, initial-scale=1">
  2376. <style type="text/css">
  2377. #outlook a { padding:0; }
  2378. body { margin:0;padding:0;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%; }
  2379. table, td { border-collapse:collapse;mso-table-lspace:0pt;mso-table-rspace:0pt; }
  2380. img { border:0;height:auto;line-height:100%; outline:none;text-decoration:none;-ms-interpolation-mode:bicubic; }
  2381. p { display:block;margin:13px 0; }
  2382. </style>
  2383. <!--[if mso]>
  2384. <xml>
  2385. <o:OfficeDocumentSettings>
  2386. <o:AllowPNG/>
  2387. <o:PixelsPerInch>96</o:PixelsPerInch>
  2388. </o:OfficeDocumentSettings>
  2389. </xml>
  2390. <![endif]-->
  2391. <!--[if lte mso 11]>
  2392. <style type="text/css">
  2393. .mj-outlook-group-fix { width:100% !important; }
  2394. </style>
  2395. <![endif]-->
  2396. <!--[if !mso]><!-->
  2397. <link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;500;600;700&display=swap" rel="stylesheet" type="text/css">
  2398. <link href="https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700" rel="stylesheet" type="text/css">
  2399. <style type="text/css">
  2400. @import url(https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;500;600;700&display=swap);
  2401. @import url(https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700);
  2402. </style>
  2403. <!--<![endif]-->
  2404. <style type="text/css">
  2405. @media only screen and (min-width:100px) {
  2406. .mj-column-px-130 { width:130px !important; max-width: 130px; }
  2407. .mj-column-per-50 { width:50% !important; max-width: 50%; }
  2408. .mj-column-per-70 { width:70% !important; max-width: 70%; }
  2409. .mj-column-per-30 { width:30% !important; max-width: 30%; }
  2410. .mj-column-per-100 { width:100% !important; max-width: 100%; }
  2411. .mj-column-px-66 { width:66px !important; max-width: 66px; }
  2412. .mj-column-px-400 { width:400px !important; max-width: 400px; }
  2413. }
  2414. </style>
  2415. <style type="text/css">
  2416. @media only screen and (max-width:100px) {
  2417. table.mj-full-width-mobile { width: 100% !important; }
  2418. td.mj-full-width-mobile { width: auto !important; }
  2419. }
  2420. </style>
  2421. </head>
  2422. <body style="word-spacing:normal;">
  2423. <div class="svgbg" style="background-image: url('https://staging.netdata.cloud/static/email/img/isotype_600.png'); background-repeat: no-repeat; background-position: top center; background-size: 600px 192px;">
  2424. <!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2425. <div style="margin:0px auto;max-width:600px;">
  2426. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
  2427. <tbody>
  2428. <tr>
  2429. <td style="direction:ltr;font-size:0px;padding:20px 0;padding-bottom:50px;padding-left:0;text-align:left;">
  2430. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:130px;" ><![endif]-->
  2431. <div class="mj-column-px-130 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:130px;">
  2432. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
  2433. <tbody>
  2434. <tr>
  2435. <td align="center" style="font-size:0px;padding:10px 25px;padding-right:0;padding-left:0;word-break:break-word;">
  2436. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
  2437. <tbody>
  2438. <tr>
  2439. <td style="width:130px;">
  2440. <img alt="Netdata Logo" height="auto" src="https://app.netdata.cloud/static/email/img/full_logo.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;" width="130">
  2441. </td>
  2442. </tr>
  2443. </tbody>
  2444. </table>
  2445. </td>
  2446. </tr>
  2447. </tbody>
  2448. </table>
  2449. </div>
  2450. <!--[if mso | IE]></td><td class="" style="vertical-align:top;width:300px;" ><![endif]-->
  2451. <div class="mj-column-per-50 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:50%;">
  2452. <table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
  2453. <tbody>
  2454. <tr>
  2455. <td style="vertical-align:top;padding-top:4px;">
  2456. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style width="100%">
  2457. <tbody>
  2458. <tr>
  2459. <td align="left" style="font-size:0px;padding:10px 25px;padding-left:10px;word-break:break-word;">
  2460. <div style="font-family:Open Sans, sans-serif;font-size:16px;line-height:1;text-align:left;color:#35414A;">Notification</div>
  2461. </td>
  2462. </tr>
  2463. </tbody>
  2464. </table>
  2465. </td>
  2466. </tr>
  2467. </tbody>
  2468. </table>
  2469. </div>
  2470. <!--[if mso | IE]></td></tr></table><![endif]-->
  2471. </td>
  2472. </tr>
  2473. </tbody>
  2474. </table>
  2475. </div>
  2476. <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="no-collapse-outlook" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2477. <div class="no-collapse" style="border-collapse: initial; margin: 0px auto; border-radius: 4px; max-width: 600px;">
  2478. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;border-radius:4px;">
  2479. <tbody>
  2480. <tr>
  2481. <td style="border:1px solid ${border_color};direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
  2482. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="set-font-outlook" width="600px" ><table align="center" border="0" cellpadding="0" cellspacing="0" class="set-font-outlook" style="width:598px;" width="598" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2483. <div class="set-font" style="font-family: 'IBM Plex Sans', sans-serif; margin: 0px auto; max-width: 598px;">
  2484. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
  2485. <tbody>
  2486. <tr>
  2487. <td style="direction:ltr;font-size:0px;padding:20px 0;padding-bottom:0;padding-top:0;text-align:center;">
  2488. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:418.6px;" ><![endif]-->
  2489. <div class="mj-column-per-70 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:70%;">
  2490. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
  2491. <tbody>
  2492. <tr>
  2493. <td align="left" style="font-size:0px;padding:10px 25px;padding-top:15px;word-break:break-word;">
  2494. <div style="font-family:Open Sans, sans-serif;font-size:20px;font-weight:700;line-height:1;text-align:left;color:#35414A;">${name}</div>
  2495. </td>
  2496. </tr>
  2497. </tbody>
  2498. </table>
  2499. </div>
  2500. <!--[if mso | IE]></td><td class="" style="vertical-align:top;width:179.4px;" ><![endif]-->
  2501. <div class="mj-column-per-30 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:30%;">
  2502. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
  2503. <tbody>
  2504. <tr>
  2505. <td align="right" style="font-size:0px;padding:10px 25px;padding-right:25px;word-break:break-word;">
  2506. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
  2507. <tbody>
  2508. <tr>
  2509. <td style="width:100px;">
  2510. <img height="auto" src="${alarm_badge}" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;" width="100"/>
  2511. </td>
  2512. </tr>
  2513. </tbody>
  2514. </table>
  2515. </td>
  2516. </tr>
  2517. </tbody>
  2518. </table>
  2519. </div>
  2520. <!--[if mso | IE]></td></tr></table><![endif]-->
  2521. </td>
  2522. </tr>
  2523. </tbody>
  2524. </table>
  2525. </div>
  2526. <!--[if mso | IE]></td></tr></table></td></tr><tr><td class="set-font-outlook" width="600px" ><table align="center" border="0" cellpadding="0" cellspacing="0" class="set-font-outlook" style="width:598px;" width="598" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2527. <div class="set-font" style="font-family: 'IBM Plex Sans', sans-serif; margin: 0px auto; max-width: 598px;">
  2528. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
  2529. <tbody>
  2530. <tr>
  2531. <td style="direction:ltr;font-size:0px;padding:0;text-align:center;">
  2532. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:598px;" ><![endif]-->
  2533. <div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
  2534. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
  2535. <tbody>
  2536. <tr>
  2537. <td align="left" style="font-size:0px;padding:10px 25px;padding-top:0;word-break:break-word;">
  2538. <div style="font-family:IBM Plex Sans, sans-serif;font-size:16px;line-height:1;text-align:left;color:#35414A;">on ${host}</div>
  2539. </td>
  2540. </tr>
  2541. </tbody>
  2542. </table>
  2543. </div>
  2544. <!--[if mso | IE]></td></tr></table><![endif]-->
  2545. </td>
  2546. </tr>
  2547. </tbody>
  2548. </table>
  2549. </div>
  2550. <!--[if mso | IE]></td></tr></table></td></tr><tr><td class="set-font-outlook" width="600px" ><table align="center" border="0" cellpadding="0" cellspacing="0" class="set-font-outlook" style="width:598px;" width="598" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2551. <div class="set-font" style="font-family: 'IBM Plex Sans', sans-serif; margin: 0px auto; max-width: 598px;">
  2552. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
  2553. <tbody>
  2554. <tr>
  2555. <td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
  2556. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:598px;" ><![endif]-->
  2557. <div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
  2558. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
  2559. <tbody>
  2560. <tr>
  2561. <td align="left" style="font-size:0px;padding:10px 25px;padding-top:0;word-break:break-word;">
  2562. <div style="font-family:Open Sans, sans-serif;font-size:26px;font-weight:700;line-height:1;text-align:left;color:#35414A;"><span style="color: ${text_color}; font-size:26px; background: ${background_color}; padding:4px 24px; border-radius: 36px">${value_string}
  2563. </span></div>
  2564. </td>
  2565. </tr>
  2566. </tbody>
  2567. </table>
  2568. </div>
  2569. <!--[if mso | IE]></td></tr></table><![endif]-->
  2570. </td>
  2571. </tr>
  2572. </tbody>
  2573. </table>
  2574. </div>
  2575. <!--[if mso | IE]></td></tr></table></td></tr><tr><td class="set-font-outlook" width="600px" ><table align="center" border="0" cellpadding="0" cellspacing="0" class="set-font-outlook" style="width:598px;" width="598" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2576. <div class="set-font" style="font-family: 'IBM Plex Sans', sans-serif; margin: 0px auto; max-width: 598px;">
  2577. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
  2578. <tbody>
  2579. <tr>
  2580. <td style="direction:ltr;font-size:0px;padding:20px 0;padding-bottom:0;padding-top:0;text-align:center;">
  2581. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:598px;" ><![endif]-->
  2582. <div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
  2583. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
  2584. <tbody>
  2585. <tr>
  2586. <td align="left" style="font-size:0px;padding:10px 25px;padding-top:0;word-break:break-word;">
  2587. <div style="font-family:Open Sans, sans-serif;font-size:16px;line-height:21px;text-align:left;color:#35414A;">Details: ${info}</div>
  2588. </td>
  2589. </tr>
  2590. </tbody>
  2591. </table>
  2592. </div>
  2593. <!--[if mso | IE]></td></tr></table><![endif]-->
  2594. </td>
  2595. </tr>
  2596. </tbody>
  2597. </table>
  2598. </div>
  2599. <!--[if mso | IE]></td></tr></table></td></tr><tr><td class="set-font-outlook" width="600px" ><table align="center" border="0" cellpadding="0" cellspacing="0" class="set-font-outlook" style="width:598px;" width="598" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2600. <div class="set-font" style="font-family: 'IBM Plex Sans', sans-serif; margin: 0px auto; max-width: 598px;">
  2601. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
  2602. <tbody>
  2603. <tr>
  2604. <td style="direction:ltr;font-size:0px;padding:20px 0;padding-bottom:0;padding-top:0;text-align:center;">
  2605. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:598px;" ><![endif]-->
  2606. <div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
  2607. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
  2608. <tbody>
  2609. <tr>
  2610. <td align="center" vertical-align="middle" style="font-size:0px;padding:10px 25px;word-break:break-word;">
  2611. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:separate;width:100%;line-height:100%;">
  2612. <tr>
  2613. <td
  2614. align="center" bgcolor="${border_color}" role="presentation" style="border:none;border-radius:3px;cursor:auto;height:44px;background:${border_color};" valign="middle">
  2615. <p style="display:block;background:${border_color};color:#ffffff;font-size:13px;font-weight:600;line-height:44px;margin:0;text-decoration:none;text-transform:none;mso-padding-alt:0px;border-radius:3px;">
  2616. <a href="${goto_url}" style="color: ${action_text_color}; text-decoration: none; width: 100%; display: inline-block">GO TO CHART</a>
  2617. </p>
  2618. </td>
  2619. </tr>
  2620. </table>
  2621. </td>
  2622. </tr>
  2623. </tbody>
  2624. </table>
  2625. </div>
  2626. <!--[if mso | IE]></td></tr></table><![endif]-->
  2627. </td>
  2628. </tr>
  2629. </tbody>
  2630. </table>
  2631. </div>
  2632. <!--[if mso | IE]></td></tr></table></td></tr></table><![endif]-->
  2633. </td>
  2634. </tr>
  2635. </tbody>
  2636. </table>
  2637. </div>
  2638. <!--[if mso | IE]></td></tr></table><![endif]-->
  2639. <div style="height:32px;line-height:32px;">&#8202;</div>
  2640. <!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="set-font-outlook" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2641. <div class="set-font" style="font-family: 'IBM Plex Sans', sans-serif; background: ${background_color}; background-color: ${background_color}; margin: 0px auto; border-radius: 4px; max-width: 600px;">
  2642. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:${background_color};background-color:${background_color};width:100%;border-radius:4px;">
  2643. <tbody>
  2644. <tr>
  2645. <td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
  2646. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
  2647. <div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
  2648. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
  2649. <tbody>
  2650. <tr>
  2651. <td align="left" style="font-size:0px;padding:10px 25px;padding-bottom:6px;word-break:break-word;">
  2652. <div style="font-family:Open Sans, sans-serif;font-size:18px;line-height:1;text-align:left;color:#35414A;">Chart:
  2653. <span style="font-weight:700; font-size:20px">${chart}</span></div>
  2654. </td>
  2655. </tr>
  2656. <tr>
  2657. <td align="left" style="font-size:0px;padding:10px 25px;padding-top:0;word-break:break-word;">
  2658. <div style="font-family:Open Sans, sans-serif;font-size:18px;line-height:1;text-align:left;color:#35414A;">Family:
  2659. <span style="font-weight:700; font-size:20px">${family}</span></div>
  2660. </td>
  2661. </tr>
  2662. <tr>
  2663. <td align="left" style="font-size:0px;padding:10px 25px;padding-top:4px;word-break:break-word;">
  2664. <div style="font-family:Open Sans, sans-serif;font-size:14px;line-height:1;text-align:left;color:#35414A;">${rich_status_raised_for}</div>
  2665. </td>
  2666. </tr>
  2667. <tr>
  2668. <td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
  2669. <p style="border-top:solid 1px lightgrey;font-size:1px;margin:0px auto;width:100%;">
  2670. </p>
  2671. <!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" style="border-top:solid 1px lightgrey;font-size:1px;margin:0px auto;width:550px;" role="presentation" width="550px" ><tr><td style="height:0;line-height:0;"> &nbsp;
  2672. </td></tr></table><![endif]-->
  2673. </td>
  2674. </tr>
  2675. <tr>
  2676. <td align="left" style="font-size:0px;padding:10px 25px;padding-bottom:6px;word-break:break-word;">
  2677. <div style="font-family:Open Sans, sans-serif;font-size:16px;line-height:1;text-align:left;color:#35414A;">On
  2678. <span style="font-weight:600">${date}</span></div>
  2679. </td>
  2680. </tr>
  2681. <tr>
  2682. <td align="left" style="font-size:0px;padding:10px 25px;padding-top:0;word-break:break-word;">
  2683. <div style="font-family:Open Sans, sans-serif;font-size:16px;line-height:1;text-align:left;color:#35414A;">By:
  2684. <span style="font-weight:600">${host}</span></div>
  2685. </td>
  2686. </tr>
  2687. <tr>
  2688. <td align="left" style="font-size:0px;padding:10px 25px;padding-top:4px;word-break:break-word;">
  2689. <div style="font-family:Open Sans, sans-serif;font-size:14px;line-height:1;text-align:left;color:#35414A;">Global time:
  2690. <span style="font-weight:600">${date_utc}</span></div>
  2691. </td>
  2692. </tr>
  2693. <tr>
  2694. <td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
  2695. <p style="border-top:solid 1px lightgrey;font-size:1px;margin:0px auto;width:100%;">
  2696. </p>
  2697. <!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" style="border-top:solid 1px lightgrey;font-size:1px;margin:0px auto;width:550px;" role="presentation" width="550px" ><tr><td style="height:0;line-height:0;"> &nbsp;
  2698. </td></tr></table><![endif]-->
  2699. </td>
  2700. </tr>
  2701. <tr>
  2702. <td align="left" style="font-size:0px;padding:10px 25px;padding-bottom:6px;word-break:break-word;">
  2703. <div style="font-family:Open Sans, sans-serif;font-size:16px;line-height:1;text-align:left;color:#35414A;">Classification:
  2704. <span style="font-weight:600">${classification}</span></div>
  2705. </td>
  2706. </tr>
  2707. <tr>
  2708. <td align="left" style="font-size:0px;padding:10px 25px;padding-top:0;word-break:break-word;">
  2709. <div style="font-family:Open Sans, sans-serif;font-size:16px;line-height:1;text-align:left;color:#35414A;">Role:
  2710. <span style="font-weight:600">${roles}</span></div>
  2711. </td>
  2712. </tr>
  2713. </tbody>
  2714. </table>
  2715. </div>
  2716. <!--[if mso | IE]></td></tr></table><![endif]-->
  2717. </td>
  2718. </tr>
  2719. </tbody>
  2720. </table>
  2721. </div>
  2722. <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="set-font-outlook" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2723. <div class="set-font" style="font-family: 'IBM Plex Sans', sans-serif; margin: 0px auto; max-width: 600px;">
  2724. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
  2725. <tbody>
  2726. <tr>
  2727. <td style="direction:ltr;font-size:0px;padding:20px 0;padding-left:25px;text-align:left;">
  2728. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:66px;" ><![endif]-->
  2729. <div class="mj-column-px-66 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:66px;">
  2730. <table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
  2731. <tbody>
  2732. <tr>
  2733. <td style="vertical-align:top;padding:0;">
  2734. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style width="100%">
  2735. <tbody>
  2736. <tr>
  2737. <td align="left" style="font-size:0px;padding:10px 25px;padding-right:0;padding-left:0;word-break:break-word;">
  2738. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
  2739. <tbody>
  2740. <tr>
  2741. <td style="width:48px;">
  2742. <img height="auto" src="https://app.netdata.cloud/static/email/img/community_icon.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;" width="48">
  2743. </td>
  2744. </tr>
  2745. </tbody>
  2746. </table>
  2747. </td>
  2748. </tr>
  2749. </tbody>
  2750. </table>
  2751. </td>
  2752. </tr>
  2753. </tbody>
  2754. </table>
  2755. </div>
  2756. <!--[if mso | IE]></td><td align="left" class="" style="vertical-align:top;width:400px;" ><![endif]-->
  2757. <div class="mj-column-px-400 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:400px;">
  2758. <table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
  2759. <tbody>
  2760. <tr>
  2761. <td style="vertical-align:top;padding-left:0;">
  2762. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style width="100%">
  2763. <tbody>
  2764. <tr>
  2765. <td align="left" style="font-size:0px;padding:10px 25px;padding-left:0;word-break:break-word;">
  2766. <div style="font-family:Open Sans, sans-serif;font-size:16px;font-weight:700;line-height:1;text-align:left;color:#35414A;">Want to know more about this alert?</div>
  2767. </td>
  2768. </tr>
  2769. <tr>
  2770. <td align="left" style="font-size:0px;padding:10px 25px;padding-left:0;word-break:break-word;">
  2771. <div style="font-family:Open Sans, sans-serif;font-size:14px;line-height:1.3;text-align:left;color:#35414A;">Discuss and troubleshoot with others on the Netdata <a href="https://community.netdata.cloud/" class="link" style="color: #00AB44; text-decoration: none;">community forums</a></div>
  2772. </td>
  2773. </tr>
  2774. </tbody>
  2775. </table>
  2776. </td>
  2777. </tr>
  2778. </tbody>
  2779. </table>
  2780. </div>
  2781. <!--[if mso | IE]></td></tr></table><![endif]-->
  2782. </td>
  2783. </tr>
  2784. </tbody>
  2785. </table>
  2786. </div>
  2787. <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="set-font-outlook" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2788. <div class="set-font" style="font-family: 'IBM Plex Sans', sans-serif; margin: 0px auto; max-width: 600px;">
  2789. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
  2790. <tbody>
  2791. <tr>
  2792. <td style="direction:ltr;font-size:0px;padding:20px 0;padding-left:25px;text-align:left;">
  2793. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:66px;" ><![endif]-->
  2794. <div class="mj-column-px-66 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:66px;">
  2795. <table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
  2796. <tbody>
  2797. <tr>
  2798. <td style="vertical-align:top;padding:0;">
  2799. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style width="100%">
  2800. <tbody>
  2801. <tr>
  2802. <td align="left" style="font-size:0px;padding:10px 25px;padding-right:0;padding-left:0;word-break:break-word;">
  2803. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
  2804. <tbody>
  2805. <tr>
  2806. <td style="width:48px;">
  2807. <img height="auto" src="https://app.netdata.cloud/static/email/img/configure_icon.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;" width="48">
  2808. </td>
  2809. </tr>
  2810. </tbody>
  2811. </table>
  2812. </td>
  2813. </tr>
  2814. </tbody>
  2815. </table>
  2816. </td>
  2817. </tr>
  2818. </tbody>
  2819. </table>
  2820. </div>
  2821. <!--[if mso | IE]></td><td align="left" class="" style="vertical-align:top;width:400px;" ><![endif]-->
  2822. <div class="mj-column-px-400 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:400px;">
  2823. <table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
  2824. <tbody>
  2825. <tr>
  2826. <td style="vertical-align:top;padding-left:0;">
  2827. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style width="100%">
  2828. <tbody>
  2829. <tr>
  2830. <td align="left" style="font-size:0px;padding:10px 25px;padding-left:0;word-break:break-word;">
  2831. <div style="font-family:Open Sans, sans-serif;font-size:16px;font-weight:700;line-height:1;text-align:left;color:#35414A;">Need to configure this alert?</div>
  2832. </td>
  2833. </tr>
  2834. <tr>
  2835. <td align="left" style="font-size:0px;padding:10px 25px;padding-left:0;word-break:break-word;">
  2836. <div style="font-family:Open Sans, sans-serif;font-size:14px;line-height:1.3;text-align:left;color:#35414A;"><span style="color: #00AB44"><a href="https://learn.netdata.cloud/docs/agent/health/notifications#:~:text=To%20edit%20it%20on%20your,have%20one%20or%20more%20destinations" class="link" style="color: #00AB44; text-decoration: none;">Edit</a></span> this alert's configuration file by logging into $s_host and running the following command:</div>
  2837. </td>
  2838. </tr>
  2839. <tr>
  2840. <td align="left" style="font-size:0px;padding:10px 25px;padding-top:8px;padding-left:0;word-break:break-word;">
  2841. <div style="font-family:Open Sans, sans-serif;font-size:12px;line-height:1.3;text-align:left;color:#35414A;">${edit_command} <br>
  2842. The alarm to edit is at line {${line}}</div>
  2843. </td>
  2844. </tr>
  2845. </tbody>
  2846. </table>
  2847. </td>
  2848. </tr>
  2849. </tbody>
  2850. </table>
  2851. </div>
  2852. <!--[if mso | IE]></td></tr></table><![endif]-->
  2853. </td>
  2854. </tr>
  2855. </tbody>
  2856. </table>
  2857. </div>
  2858. <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="history-wrapper-outlook" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2859. <div class="history-wrapper" style="background: #F7F8F8; background-color: #F7F8F8; margin: 0px auto; max-width: 100%;">
  2860. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#F7F8F8;background-color:#F7F8F8;width:100%;">
  2861. <tbody>
  2862. <tr>
  2863. <td style="direction:ltr;font-size:0px;padding:0;padding-bottom:24px;text-align:center;">
  2864. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="set-font-outlook" width="600px" ><table align="center" border="0" cellpadding="0" cellspacing="0" class="set-font-outlook" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2865. <div class="set-font" style="font-family: 'IBM Plex Sans', sans-serif; margin: 0px auto; max-width: 600px;">
  2866. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
  2867. <tbody>
  2868. <tr>
  2869. <td style="direction:ltr;font-size:0px;padding:20px 0;padding-bottom:12px;text-align:center;">
  2870. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
  2871. <div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
  2872. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
  2873. <tbody>
  2874. <tr>
  2875. <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
  2876. <div style="font-family:Open Sans, sans-serif;font-size:16px;line-height:1;text-align:center;color:#35414A;">The node has
  2877. <span style="font-weight:600">${total_warnings} warning</span>
  2878. and
  2879. <span style="font-weight:600">${total_critical} critical</span>
  2880. additional active alert(s)</div>
  2881. </td>
  2882. </tr>
  2883. </tbody>
  2884. </table>
  2885. </div>
  2886. <!--[if mso | IE]></td></tr></table><![endif]-->
  2887. </td>
  2888. </tr>
  2889. </tbody>
  2890. </table>
  2891. </div>
  2892. ${CRIT_ALARMS}
  2893. ${WARN_ALARMS}
  2894. <!--[if mso | IE]></td></tr></table></td></tr></table><![endif]-->
  2895. </td>
  2896. </tr>
  2897. </tbody>
  2898. </table>
  2899. </div>
  2900. <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="set-font-outlook" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
  2901. <div class="set-font" style="font-family: 'IBM Plex Sans', sans-serif; margin: 0px auto; max-width: 600px;">
  2902. <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
  2903. <tbody>
  2904. <tr>
  2905. <td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
  2906. <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
  2907. <div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
  2908. <table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
  2909. <tbody>
  2910. <tr>
  2911. <td style="vertical-align:top;padding-top:44px;padding-bottom:12px;">
  2912. <table border="0" cellpadding="0" cellspacing="0" role="presentation" style width="100%">
  2913. <tbody>
  2914. <tr>
  2915. <td align="left" style="font-size:0px;padding:10px 25px;padding-top:0;padding-bottom:0;word-break:break-word;">
  2916. <div style="font-family:Open Sans, sans-serif;font-size:13px;line-height:1;text-align:center;color:#35414A;">© Netdata 2021 - The real-time performance and health monitoring</div>
  2917. </td>
  2918. </tr>
  2919. </tbody>
  2920. </table>
  2921. </td>
  2922. </tr>
  2923. </tbody>
  2924. </table>
  2925. </div>
  2926. <!--[if mso | IE]></td></tr></table><![endif]-->
  2927. </td>
  2928. </tr>
  2929. </tbody>
  2930. </table>
  2931. </div>
  2932. <!--[if mso | IE]></td></tr></table><![endif]-->
  2933. </div>
  2934. </body>
  2935. </html>
  2936. EOF
  2937. send_email <<EOF
  2938. To: ${to_email}
  2939. Subject: ${html_email_subject}
  2940. MIME-Version: 1.0
  2941. Content-Type: multipart/alternative; boundary="multipart-boundary"
  2942. ${email_thread_headers}
  2943. This is a MIME-encoded multipart message
  2944. --multipart-boundary
  2945. ${email_plaintext_part}
  2946. --multipart-boundary
  2947. ${email_html_part}
  2948. --multipart-boundary--
  2949. EOF
  2950. fi
  2951. SENT_EMAIL=$?
  2952. # -----------------------------------------------------------------------------
  2953. # send the EVENT to Dynatrace
  2954. send_dynatrace "${host}" "${chart}" "${name}" "${status}"
  2955. SENT_DYNATRACE=$?
  2956. # -----------------------------------------------------------------------------
  2957. # send the EVENT to Stackpulse
  2958. send_stackpulse
  2959. SENT_STACKPULSE=$?
  2960. # -----------------------------------------------------------------------------
  2961. # send messages to Opsgenie
  2962. send_opsgenie
  2963. SENT_OPSGENIE=$?
  2964. # -----------------------------------------------------------------------------
  2965. # let netdata know
  2966. for state in "${SENT_EMAIL}" \
  2967. "${SENT_PUSHOVER}" \
  2968. "${SENT_TELEGRAM}" \
  2969. "${SENT_SLACK}" \
  2970. "${SENT_HANGOUTS}" \
  2971. "${SENT_ROCKETCHAT}" \
  2972. "${SENT_ALERTA}" \
  2973. "${SENT_FLOCK}" \
  2974. "${SENT_DISCORD}" \
  2975. "${SENT_TWILIO}" \
  2976. "${SENT_HIPCHAT}" \
  2977. "${SENT_MESSAGEBIRD}" \
  2978. "${SENT_KAVENEGAR}" \
  2979. "${SENT_PUSHBULLET}" \
  2980. "${SENT_KAFKA}" \
  2981. "${SENT_PD}" \
  2982. "${SENT_FLEEP}" \
  2983. "${SENT_PROWL}" \
  2984. "${SENT_CUSTOM}" \
  2985. "${SENT_IRC}" \
  2986. "${SENT_AWSSNS}" \
  2987. "${SENT_MATRIX}" \
  2988. "${SENT_SYSLOG}" \
  2989. "${SENT_SMS}" \
  2990. "${SENT_MSTEAMS}" \
  2991. "${SENT_DYNATRACE}" \
  2992. "${SENT_STACKPULSE}" \
  2993. "${SENT_OPSGENIE}"; do
  2994. if [ "${state}" -eq 0 ]; then
  2995. # we sent something
  2996. exit 0
  2997. fi
  2998. done
  2999. # we did not send anything
  3000. exit 1