]> arthur.barton.de Git - netdata.git/blob - plugins.d/charts.d.plugin
updated docs; nut.chart.sh now autodetects multiple UPSes, web and cache directories...
[netdata.git] / plugins.d / charts.d.plugin
1 #!/bin/bash
2
3 PROGRAM_FILE="$0"
4 PROGRAM_NAME="charts.d"
5 echo >&2 "$PROGRAM_NAME: started from '$PROGRAM_FILE' with options: $*"
6
7 if [ $(( ${BASH_VERSINFO[0]} )) -lt 4 ]
8 then
9         echo >&2 
10         echo >&2 "$PROGRAM_NAME: ERROR"
11         echo >&2 "BASH version 4 or later is required."
12         echo >&2 "You are running version: ${BASH_VERSION}"
13         echo >&2 "Please upgrade."
14         echo >&2 
15         exit 1
16 fi
17
18 # check a few commands
19 require_cmd() {
20         which "$1" >/dev/null
21         if [ $? -ne 0 ]
22                 then
23                 echo >&2 "$PROGRAM_NAME: ERROR: Command '$1' is not found in the system path."
24                 return 1
25         fi
26         return 0
27 }
28
29 require_cmd date || exit 1
30 require_cmd sed || exit 1
31 require_cmd basename || exit 1
32 require_cmd dirname || exit 1
33 require_cmd cat || exit 1
34 require_cmd grep || exit 1
35 require_cmd mktemp || exit 1
36
37 # -----------------------------------------------------------------------------
38 # insternal defaults
39 # netdata exposes a few environment variables for us
40
41 pause_method="sleep" # use either "suspend" or "sleep"
42                      # DO NOT USE SUSPEND - LINUX WILL SUSPEND NETDATA TOO
43                      # THE WHOLE PROCESS GROUP - NOT JUST THE SHELL
44
45 pluginsd="${NETDATA_PLUGINS_DIR}"
46 [ -z "$pluginsd" ] && pluginsd="$( dirname $PROGRAM_FILE )"
47
48 confd="${NETDATA_CONFIG_DIR-/etc/netdata}"
49 chartsd="$pluginsd/../charts.d"
50 myconfig="$confd/charts.d.conf"
51
52 minimum_update_frequency="${NETDATA_UPDATE_EVERY-1}"
53 update_every=${minimum_update_frequency}        # this will be overwritten by the command line
54
55 # work around for non BASH shells
56 charts_create="_create"
57 charts_update="_update"
58 charts_check="_check"
59 charts_undescore="_"
60
61 # -----------------------------------------------------------------------------
62 # parse parameters
63
64 debug=0
65 check=0
66 chart_only=
67 while [ ! -z "$1" ]
68 do
69         if [ "$1" = "check" ]
70         then
71                 check=1
72                 shift
73                 continue
74         fi
75
76         if [ "$1" = "debug" -o "$1" = "all" ]
77         then
78                 debug=1
79                 shift
80                 continue
81         fi
82
83         if [ -f "$chartsd/$1.chart.sh" ]
84         then
85                 debug=1
86                 chart_only="$( echo $1.chart.sh | sed "s/\.chart\.sh$//g" )"
87                 shift
88                 continue
89         fi
90
91         if [ -f "$chartsd/$1" ]
92         then
93                 debug=1
94                 chart_only="$( echo $1 | sed "s/\.chart\.sh$//g" )"
95                 shift
96                 continue
97         fi
98
99         # number check
100         n="$1"
101         x=$(( n ))
102         if [ "$x" = "$n" ]
103         then
104                 shift
105                 update_every=$x
106                 [ $update_every -lt $minimum_update_frequency ] && update_every=$minimum_update_frequency
107                 continue
108         fi
109
110         echo >&2 "Cannot understand parameter $1. Aborting."
111         echo "DISABLE"
112         exit 1
113 done
114
115
116 # -----------------------------------------------------------------------------
117 # load my configuration
118
119 if [ -f "$myconfig" ]
120         then
121         . "$myconfig"
122         if [ $? -ne 0 ]
123         then
124                 echo >&2 "$PROGRAM_NAME: cannot load $myconfig"
125                 echo "DISABLE"
126                 exit 1
127         fi
128 fi
129
130 if [ "$pause_method" = "suspend" ]
131 then
132         # enable bash job control
133         # this is required for suspend to work
134         set -m
135 fi
136
137 # -----------------------------------------------------------------------------
138 # internal checks
139
140 # netdata passes the requested update frequency as the first argument
141 update_every=$(( update_every + 1 - 1)) # makes sure it is a number
142 test $update_every -eq 0 && update_every=1 # if it is zero, make it 1
143
144 # check the charts.d directory
145 if [ ! -d "$chartsd" ]
146         then
147         echo >&2 "$PROGRAM_NAME: cannot find charts directory '$chartsd'"
148         echo "DISABLE"
149 fi
150
151
152 # -----------------------------------------------------------------------------
153 # loop control
154
155 # default sleep function
156 loopsleepms() {
157         sleep $1
158 }
159
160 now_ms=
161 current_time_ms() {
162         now_ms="$(date +'%s')000"
163 }
164
165 # if found and included, this file overwrites loopsleepms()
166 # and current_time_ms() with a high resolution timer function
167 # for precise looping.
168 . "$pluginsd/loopsleepms.sh.inc"
169
170
171 # -----------------------------------------------------------------------------
172 # charts check functions
173
174 all_charts() {
175         cd "$chartsd"
176         [ $? -ne 0 ] && echo >&2 "$PROGRAM_NAME: Cannot cd to $chartsd" && return 1
177
178         ls *.chart.sh | sed "s/\.chart\.sh$//g"
179 }
180
181 all_enabled_charts() {
182         local charts=
183
184         # find all enabled charts
185
186         for chart in $( all_charts )
187         do
188                 eval "enabled=\$$chart"
189                 if [ "$enabled" = "yes" ]
190                 then
191                         [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: '$chart' is enabled."
192                         local charts="$charts $chart"
193                 else
194                         echo >&2 "$PROGRAM_NAME: '$chart' is NOT enabled. Add a line with $chart=yes in $myconfig to enable it."
195                 fi
196         done
197
198         local charts2=
199         for chart in $charts
200         do
201                 # check the enabled charts
202                 local check="$( cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_check()" )"
203                 if [ -z "$check" ]
204                 then
205                         echo >&2 "$PROGRAM_NAME: chart '$chart' does not seem to have a $chart$charts_check() function. Disabling it."
206                         continue
207                 fi
208
209                 local create="$( cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_create()" )"
210                 if [ -z "$create" ]
211                 then
212                         echo >&2 "$PROGRAM_NAME: chart '$chart' does not seem to have a $chart$charts_create() function. Disabling it."
213                         continue
214                 fi
215
216                 local update="$( cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_update()" )"
217                 if [ -z "$update" ]
218                 then
219                         echo >&2 "$PROGRAM_NAME: chart '$chart' does not seem to have a $chart$charts_update() function. Disabling it."
220                         continue
221                 fi
222
223                 # check its config
224                 #if [ -f "$confd/$chart.conf" ]
225                 #then
226                 #       if [ ! -z "$( cat "$confd/$chart.conf" | sed "s/^ \+//g" | grep -v "^$" | grep -v "^#" | grep -v "^$chart$charts_undescore" )" ]
227                 #       then
228                 #               echo >&2 "$PROGRAM_NAME: chart's $chart config $confd/$chart.conf should only have lines starting with $chart$charts_undescore . Disabling it."
229                 #               continue
230                 #       fi
231                 #fi
232
233                 "$pluginsd/charts.d.dryrun-helper.sh" "$chart" "$chartsd/$chart.chart.sh" "$confd/$chart.conf" >/dev/null
234                 if [ $? -ne 0 ]
235                 then
236                         echo >&2 "$PROGRAM_NAME: chart's $chart did not pass the dry run check. This means it uses global variables not starting with $chart. Disabling it."
237                         continue
238                 fi
239
240                 local charts2="$charts2 $chart"
241         done
242
243         echo $charts2
244         [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: enabled charts: $charts2"
245 }
246
247
248 # -----------------------------------------------------------------------------
249 # load the charts
250
251 suffix_update_every="_update_every"
252 active_charts=
253 for chart in $( all_enabled_charts )
254 do
255         [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: loading chart: '$chartsd/$chart.chart.sh'"
256         . "$chartsd/$chart.chart.sh"
257
258         if [ -f "$confd/$chart.conf" ]
259         then
260                 [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: loading chart options: '$confd/$chart.conf'"
261                 . "$confd/$chart.conf"
262         fi
263
264         eval "dt=\$$chart$suffix_update_every"
265         dt=$(( dt + 1 - 1 )) # make sure it is a number
266         if [ $dt -lt $update_every ]
267         then
268                 eval "$chart$suffix_update_every=$update_every"
269         fi
270
271         $chart$charts_check
272         if [ $? -eq 0 ]
273         then
274                 [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: '$chart' activated"
275                 active_charts="$active_charts $chart"
276         else
277                 echo >&2 "$PROGRAM_NAME: chart '$chart' check() function reports failure."
278         fi
279 done
280 [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: activated charts: $active_charts"
281
282
283 # -----------------------------------------------------------------------------
284 # check overwrites
285
286 # enable work time reporting
287 debug_time=
288 test $debug -eq 1 && debug_time=tellwork
289
290 # if we only need a specific chart, remove all the others
291 if [ ! -z "${chart_only}" ]
292 then
293         [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: requested to run only for: '${chart_only}'"
294         check_charts=
295         for chart in $active_charts
296         do
297                 if [ "$chart" = "$chart_only" ]
298                 then
299                         check_charts="$chart"
300                         break
301                 fi
302         done
303         active_charts="$check_charts"
304 fi
305 [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: activated charts: $active_charts"
306
307 # stop if we just need a pre-check
308 if [ $check -eq 1 ]
309 then
310         echo >&2 "CHECK RESULT"
311         echo >&2 "Will run the charts: $active_charts"
312         exit 0
313 fi
314
315 # -----------------------------------------------------------------------------
316 # create temp dir
317
318 TMP_DIR=
319 chartsd_cleanup() {
320         cd /tmp
321         if [ ! -z "$TMP_DIR" -a -d "$TMP_DIR" ]
322         then
323                 [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: cleaning up temporary directory $TMP_DIR ..."
324                 rm -rf "$TMP_DIR"
325         fi
326         exit 0
327 }
328 trap chartsd_cleanup EXIT
329 trap chartsd_cleanup SIGHUP
330 trap chartsd_cleanup INT
331
332 if [ $UID = "0" ]
333 then
334         TMP_DIR="$( mktemp -d /var/run/netdata-charts.d-XXXXXXXXXX )"
335 else
336         TMP_DIR="$( mktemp -d /tmp/.netdata-charts.d-XXXXXXXXXX )"
337 fi
338
339 cd "$TMP_DIR" || exit 1
340
341 # -----------------------------------------------------------------------------
342 # library functions
343
344 fixid() {
345         echo "$*" |\
346                 tr -c "[A-Z][a-z][0-9]" "_" |\
347                 sed -e "s|^_\+||g" -e "s|_\+$||g" -e "s|_\+|_|g" |\
348                 tr "[A-Z]" "[a-z]"
349 }
350
351
352 # -----------------------------------------------------------------------------
353 # create charts
354
355 run_charts=
356 for chart in $active_charts
357 do
358         [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: Calling '$chart$charts_create()'..."
359         $chart$charts_create
360         if [ $? -eq 0 ]
361         then
362                 run_charts="$run_charts $chart"
363                 [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: '$chart' has initialized."
364         else
365                 echo >&2 "$PROGRAM_NAME: chart '$chart' function '$chart$charts_create()' reports failure."
366         fi
367 done
368 [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: run_charts='$run_charts'"
369
370
371 # -----------------------------------------------------------------------------
372 # update dimensions
373
374 declare -A charts_last_update=() charts_min_dt=()
375 global_update() {
376         local exit_after=$((3600 / update_every)) \
377                 c=0 dt \
378                 chart now_charts=() next_charts=($run_charts)
379
380         # return the current time in ms in $now_ms
381         current_time_ms
382
383         for chart in $run_charts
384         do
385                 eval "charts_min_dt[$chart]=\$$chart$suffix_update_every"
386                 test -z "${charts_min_dt[$chart]}" && charts_min_dt[$charts]=$update_every
387                 charts_last_update[$chart]=$((now_ms - (charts_min_dt[$chart] * 1000) ))
388         done
389
390         # the main loop
391         while [ 1 ]
392         do
393                 c=$((c + 1))
394                 now_charts=("${next_charts[@]}")
395                 next_charts=()
396
397                 for chart in "${now_charts[@]}"
398                 do
399                         # return the current time in ms in $now_ms
400                         current_time_ms
401
402                         dt=$(( (now_ms - charts_last_update[$chart]) * 1000 ))
403                         if [ $dt -ge $(( charts_min_dt[$chart] * 1000000 )) ]
404                         then
405                                 charts_last_update[$chart]=$now_ms
406
407                                 # the first call should not give a duration
408                                 # so that netdata calibrates to current time
409                                 test $c -eq 1 && dt=
410
411                                 $chart$charts_update $dt
412                                 if [ $? -eq 0 ]
413                                 then
414                                         next_charts+=($chart)
415                                 else
416                                         echo >&2 "$PROGRAM_NAME: chart '$chart' update() function reports failure. Disabling it."
417                                 fi
418                         else
419                                 next_charts+=($chart)
420                         fi
421                 done
422
423                 if [ "$pause_method" = "suspend" ]
424                 then
425                         echo "STOPPING_WAKE_ME_UP_PLEASE"
426                         suspend || ( echo >&2 "$PROGRAM_NAME: suspend returned error $?, falling back to sleep."; loopsleepms $debug_time $update_every )
427                 else
428                         # wait the time you are required to
429                         loopsleepms $debug_time $update_every
430                 fi
431
432                 test $c -gt $exit_after && exit 0
433         done
434 }
435
436 global_update