projects/guix·blob kernel-updates · refs

1747 lines · 63.5 KiB

1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2018 Sou Bunnbu <[email protected]>
3;;; Copyright © 2018, 2019 Gábor Boskovits <[email protected]>
4;;; Copyright © 2018, 2019, 2020 Oleg Pykhalov <[email protected]>
5;;; Copyright © 2022 Marius Bakke <[email protected]>
6;;; Copyright © 2023 Bruno Victal <[email protected]>
7;;;
8;;; This file is part of GNU Guix.
9;;;
10;;; GNU Guix is free software; you can redistribute it and/or modify it
11;;; under the terms of the GNU General Public License as published by
12;;; the Free Software Foundation; either version 3 of the License, or (at
13;;; your option) any later version.
14;;;
15;;; GNU Guix is distributed in the hope that it will be useful, but
16;;; WITHOUT ANY WARRANTY; without even the implied warranty of
17;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;;; GNU General Public License for more details.
19;;;
20;;; You should have received a copy of the GNU General Public License
21;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
22
23(define-module (gnu services monitoring)
24 #:use-module (gnu packages admin)
25 #:use-module (gnu packages bash)
26 #:use-module (gnu packages monitoring)
27 #:use-module (gnu packages networking)
28 #:use-module (gnu packages python)
29 #:use-module (gnu services configuration)
30 #:use-module (gnu services shepherd)
31 #:use-module (gnu services web)
32 #:use-module (gnu services)
33 #:use-module (gnu system shadow)
34 #:use-module (guix gexp)
35 #:use-module ((guix modules) #:select (source-module-closure))
36 #:use-module (guix packages)
37 #:use-module ((guix profiles) #:select (packages->manifest
38 profile
39 profile-search-paths))
40 #:use-module (guix records)
41 #:use-module ((guix search-paths) #:select (search-path-specification-variable))
42 #:use-module ((guix self) #:select (make-config.scm))
43 #:use-module ((guix ui) #:select (display-hint G_))
44 #:use-module (guix utils)
45 #:use-module (ice-9 match)
46 #:use-module (ice-9 rdelim)
47 #:use-module (srfi srfi-1)
48 #:use-module ((srfi srfi-2) #:select (and-let*))
49 #:use-module (srfi srfi-26)
50 #:use-module (srfi srfi-35)
51 #:use-module ((srfi srfi-171) #:select (list-transduce
52 rcons
53 tconcatenate
54 tfilter
55 tmap))
56
57 #:export (darkstat-configuration
58 darkstat-service-type
59
60 prometheus-node-exporter-configuration
61 prometheus-node-exporter-configuration?
62 prometheus-node-exporter-configuration-package
63 prometheus-node-exporter-web-listen-address
64 prometheus-node-exporter-service-type
65
66 vnstat-configuration
67 vnstat-configuration?
68 vnstat-service-type
69 vnstat-configuration-package
70 vnstat-configuration-database-directory
71 vnstat-configuration-5-minute-hours
72 vnstat-configuration-64bit-interface-counters
73 vnstat-configuration-always-add-new-interfaces?
74 vnstat-configuration-bandwidth-detection?
75 vnstat-configuration-bandwidth-detection-interval
76 vnstat-configuration-boot-variation
77 vnstat-configuration-check-disk-space?
78 vnstat-configuration-create-directories?
79 vnstat-configuration-daemon-group
80 vnstat-configuration-daemon-user
81 vnstat-configuration-daily-days
82 vnstat-configuration-database-synchronous
83 vnstat-configuration-database-write-ahead-logging?
84 vnstat-configuration-hourly-days
85 vnstat-configuration-log-file
86 vnstat-configuration-max-bandwidth
87 vnstat-configuration-max-bw
88 vnstat-configuration-monthly-months
89 vnstat-configuration-month-rotate
90 vnstat-configuration-month-rotate-affects-years?
91 vnstat-configuration-offline-save-interval
92 vnstat-configuration-pid-file
93 vnstat-configuration-poll-interval
94 vnstat-configuration-rescan-database-on-save?
95 vnstat-configuration-save-interval
96 vnstat-configuration-save-on-status-change?
97 vnstat-configuration-time-sync-wait
98 vnstat-configuration-top-day-entries
99 vnstat-configuration-trafficless-entries?
100 vnstat-configuration-update-file-owner?
101 vnstat-configuration-update-interval
102 vnstat-configuration-use-logging
103 vnstat-configuration-use-utc?
104 vnstat-configuration-yearly-years
105
106 zabbix-server-configuration
107 zabbix-server-service-type
108 zabbix-agent-configuration
109 zabbix-agent-service-type
110 zabbix-front-end-configuration
111 zabbix-front-end-service-type
112 %zabbix-front-end-configuration-nginx
113
114 collectd-plugin-generic
115 collectd-plugin-generic?
116 collectd-plugin-generic-load-plugin?
117 collectd-plugin-generic-name
118 collectd-plugin-generic-options
119
120 collectd-plugin-python
121 collectd-plugin-python?
122 collectd-plugin-python-load-plugin?
123 collectd-plugin-python-type-databases
124 collectd-plugin-python-type-packages
125 collectd-plugin-python-module-paths
126 collectd-plugin-python-log-traces?
127 collectd-plugin-python-log-interactive?
128 collectd-plugin-python-import
129 collectd-plugin-python-module
130
131 collectd-plugin?
132
133 %collectd-default-type-database
134 %collectd-pid-file
135
136 collectd-configuration
137 collectd-configuration?
138 collectd-configuration-collectd
139 collectd-configuration-base-directory
140 collectd-configuration-auto-load-plugins?
141 collectd-configuration-collect-internal-stats?
142 collectd-configuration-type-databases
143 collectd-configuration-interval
144 collectd-configuration-max-read-interval
145 collectd-configuration-timeout
146 collectd-configuration-read-threads
147 collectd-configuration-write-threads
148 collectd-configuration-write-queue-limit-high
149 collectd-configuration-write-queue-limit-low
150 collectd-configuration-host-name
151 collectd-configuration-fully-qualified-domain-name-lookup?
152 collectd-configuration-plugins
153
154 collectd-service-type))
155
156
157;;;
158;;; darkstat
159;;;
160
161(define-record-type* <darkstat-configuration>
162 darkstat-configuration make-darkstat-configuration darkstat-configuration?
163 (package darkstat-configuration-package
164 (default darkstat))
165 (interface darkstat-configuration-interface)
166 (port darkstat-configuration-port
167 (default "667"))
168 (bind-address darkstat-configuration-bind-address
169 (default "127.0.0.1"))
170 (base darkstat-configuration-base
171 (default "/")))
172
173(define %darkstat-accounts
174 (list (user-account
175 (name "darkstat")
176 (group "darkstat")
177 (system? #t)
178 (comment "darkstat daemon user")
179 (home-directory "/var/lib/darkstat")
180 (shell (file-append shadow "/sbin/nologin")))
181 (user-group
182 (name "darkstat")
183 (system? #t))))
184
185(define darkstat-shepherd-service
186 (match-lambda
187 (($ <darkstat-configuration>
188 package interface port bind-address base)
189 (shepherd-service
190 (documentation "Network statistics gatherer.")
191 (provision '(darkstat))
192 (requirement '(user-processes networking))
193 (start #~(make-forkexec-constructor
194 (list #$(file-append package "/sbin/darkstat")
195 "-i" #$interface
196 "-p" #$port
197 "-b" #$bind-address
198 "--base" #$base
199 "--syslog" "--no-daemon"
200 "--chroot" "/var/lib/darkstat"
201 "--user" "darkstat"
202 "--import" "darkstat.db"
203 "--export" "darkstat.db")))
204 (stop #~(make-kill-destructor))))))
205
206(define darkstat-service-type
207 (service-type
208 (name 'darkstat)
209 (description
210 "Run @command{darkstat} to serve network traffic statistics reports over
211HTTP.")
212 (extensions
213 (list (service-extension account-service-type
214 (const %darkstat-accounts))
215 (service-extension shepherd-root-service-type
216 (compose list darkstat-shepherd-service))))))
217
218
219;;;
220;;; Prometheus node exporter
221;;;
222
223(define-record-type* <prometheus-node-exporter-configuration>
224 prometheus-node-exporter-configuration
225 make-prometheus-node-exporter-configuration
226 prometheus-node-exporter-configuration?
227 (package prometheus-node-exporter-configuration-package
228 (default prometheus-node-exporter))
229 (web-listen-address prometheus-node-exporter-web-listen-address
230 (default ":9100"))
231 (textfile-directory prometheus-node-exporter-textfile-directory
232 (default "/var/lib/prometheus/node-exporter"))
233 (extra-options prometheus-node-exporter-extra-options
234 (default '())))
235
236(define %prometheus-node-exporter-accounts
237 (list (user-account
238 (name "prometheus-node-exporter")
239 (group "prometheus-node-exporter")
240 (system? #t)
241 (comment "Prometheus node exporter daemon user")
242 (home-directory "/var/empty")
243 (shell (file-append shadow "/sbin/nologin")))
244 (user-group
245 (name "prometheus-node-exporter")
246 (system? #t))))
247
248(define prometheus-node-exporter-shepherd-service
249 (match-lambda
250 (( $ <prometheus-node-exporter-configuration>
251 package web-listen-address textfile-directory extra-options)
252 (list
253 (shepherd-service
254 (documentation "Prometheus node exporter.")
255 (provision '(prometheus-node-exporter))
256 (requirement '(user-processes networking))
257 (start #~(make-forkexec-constructor
258 (list #$(file-append package "/bin/node_exporter")
259 "--web.listen-address" #$web-listen-address
260 #$@(if textfile-directory
261 (list "--collector.textfile.directory"
262 textfile-directory)
263 '())
264 #$@extra-options)
265 #:user "prometheus-node-exporter"
266 #:group "prometheus-node-exporter"
267 #:log-file "/var/log/prometheus-node-exporter.log"))
268 (stop #~(make-kill-destructor)))))))
269
270(define (prometheus-node-exporter-activation config)
271 (with-imported-modules '((guix build utils))
272 #~(let ((textfile-directory
273 #$(prometheus-node-exporter-textfile-directory config)))
274 (use-modules (guix build utils))
275
276 (when textfile-directory
277 (let ((user (getpw "prometheus-node-exporter")))
278 #t
279 (mkdir-p textfile-directory)
280 (chown textfile-directory (passwd:uid user) (passwd:gid user))
281 (chmod textfile-directory #o775))))))
282
283(define prometheus-node-exporter-service-type
284 (service-type
285 (name 'prometheus-node-exporter)
286 (description
287 "Run @command{node_exporter} to serve hardware and OS metrics to
288Prometheus.")
289 (extensions
290 (list
291 (service-extension account-service-type
292 (const %prometheus-node-exporter-accounts))
293 (service-extension activation-service-type
294 prometheus-node-exporter-activation)
295 (service-extension shepherd-root-service-type
296 prometheus-node-exporter-shepherd-service)))
297 (default-value (prometheus-node-exporter-configuration))))
298
299
300;;;
301;;; vnstat daemon
302;;;
303
304(define* (camelfy-field-name field-name #:key (dromedary? #f))
305 (match (string-split (symbol->string field-name) #\-)
306 ((head tail ...)
307 (string-join (cons (if dromedary? head (string-upcase head 0 1))
308 (map (cut string-upcase <> 0 1) tail)) ""))))
309
310(define (strip-trailing-?-character field-name)
311 "Drop rightmost '?' character"
312 (let ((str (symbol->string field-name)))
313 (if (string-suffix? "?" str)
314 (string->symbol (string-drop-right str 1))
315 field-name)))
316
317(define (vnstat-serialize-string field-name value)
318 #~(format #f "~a ~s~%"
319 #$(camelfy-field-name field-name)
320 #$value))
321
322(define vnstat-serialize-integer vnstat-serialize-string)
323
324(define (vnstat-serialize-boolean field-name value)
325 #~(format #f "~a ~a~%"
326 #$(camelfy-field-name (strip-trailing-?-character field-name))
327 #$(if value 1 0)))
328
329(define (vnstat-serialize-alist field-name value)
330 (generic-serialize-alist string-append
331 (lambda (iface val)
332 (vnstat-serialize-integer
333 (format #f "MaxBW~a" iface) val))
334 value))
335
336(define (vnstat-serialize-user-account field-name value)
337 (vnstat-serialize-string field-name (user-account-name value)))
338
339(define (vnstat-serialize-user-group field-name value)
340 (vnstat-serialize-string field-name (user-group-name value)))
341
342(define-maybe string (prefix vnstat-))
343(define-maybe integer (prefix vnstat-))
344(define-maybe boolean (prefix vnstat-))
345(define-maybe alist (prefix vnstat-))
346(define-maybe user-account (prefix vnstat-))
347(define-maybe user-group (prefix vnstat-))
348
349(define %vnstat-user
350 (user-account
351 (name "vnstat")
352 (group "vnstat")
353 (system? #t)
354 (home-directory "/var/empty")
355 (shell (file-append shadow "/sbin/nologin"))))
356
357(define %vnstat-group
358 (user-group
359 (name "vnstat")
360 (system? #t)))
361
362;; Documentation strings from vnstat.conf manpage adapted to texinfo.
363;; vnstat checkout: v2.10, commit b3408af1c609aa6265d296cab7bfe59a61d7cf70
364;; Do not reflow these strings or drop the initial \ escape as it makes it
365;; harder to diff against the manpage.
366(define-configuration vnstat-configuration
367 (package
368 (file-like vnstat)
369 "The vnstat package."
370 empty-serializer)
371
372 (database-directory
373 (string "/var/lib/vnstat")
374 "\
375Specifies the directory where the database is to be stored.
376A full path must be given and a leading '/' isn't required."
377 (serializer
378 (lambda (_ value)
379 (vnstat-serialize-string 'database-dir value))))
380
381 (5-minute-hours
382 (maybe-integer 48)
383 "\
384Data retention duration for the 5 minute resolution entries. The configuration
385defines for how many past hours entries will be stored. Set to @code{-1} for
386unlimited entries or to @code{0} to disable the data collection of this
387resolution.")
388
389 (64bit-interface-counters
390 (maybe-integer -2)
391 "\
392Select interface counter handling. Set to @code{1} for defining that all interfaces
393use 64-bit counters on the kernel side and @code{0} for defining 32-bit counter. Set
394to @code{-1} for using the old style logic used in earlier versions where counter
395values within 32-bits are assumed to be 32-bit and anything larger is assumed to
396be a 64-bit counter. This may produce false results if a 64-bit counter is
397reset within the 32-bits. Set to @code{-2} for using automatic detection based on
398available kernel datastructures.")
399
400 (always-add-new-interfaces?
401 (maybe-boolean #t)
402 "\
403Enable or disable automatic creation of new database entries for interfaces not
404currently in the database even if the database file already exists when the
405daemon is started. New database entries will also get created for new interfaces
406seen while the daemon is running. Pseudo interfaces @samp{lo}, @samp{lo0} and @samp{sit0} are always
407excluded from getting added.")
408
409 (bandwidth-detection?
410 (maybe-boolean #t)
411 "\
412Try to automatically detect
413@var{max-bandwidth}
414value for each monitored interface. Mostly only ethernet interfaces support
415this feature.
416@var{max-bandwidth}
417will be used as fallback value if detection fails. Any interface specific
418@var{max-BW}
419configuration will disable the detection for the specified interface.
420In Linux, the detection is disabled for tun interfaces due to the
421Linux kernel always reporting 10 Mbit regardless of the used real interface.")
422
423 (bandwidth-detection-interval
424 (maybe-integer 5)
425 "\
426How often in minutes interface specific detection of
427@var{max-bandwidth}
428is done for detecting possible changes when
429@var{bandwidth-detection}
430is enabled. Can be disabled by setting to @code{0}. Value range: @samp{0}..@samp{30}")
431
432 (boot-variation
433 (maybe-integer 15)
434 "\
435Time in seconds how much the boot time reported by system kernel can variate
436between updates. Value range: @samp{0}..@samp{300}")
437
438 (check-disk-space?
439 (maybe-boolean #t)
440 "\
441Enable or disable the availability check of at least some free disk space before
442a database write.")
443
444 (create-directories?
445 (maybe-boolean #t)
446 "\
447Enable or disable the creation of directories when a configured path doesn't
448exist. This includes @var{database-directory}."
449 (serializer
450 (lambda (_ value)
451 (if (maybe-value-set? value)
452 (vnstat-serialize-boolean 'create-dirs value) ""))))
453
454 ;; Note: Documentation for daemon-group and daemon-user adapted
455 ;; for user-group and user-account record-types.
456 (daemon-group
457 (maybe-user-group %vnstat-group)
458 "\
459Specify the group to which the daemon process should switch during startup.
460Set to @code{%unset-value} to disable group switching.")
461
462 (daemon-user
463 (maybe-user-account %vnstat-user)
464 "\
465Specify the user to which the daemon process should switch during startup.
466Set to @code{%unset-value} to disable user switching.")
467
468 (daily-days
469 (maybe-integer 62)
470 "\
471Data retention duration for the one day resolution entries. The configuration
472defines for how many past days entries will be stored. Set to @code{-1} for
473unlimited entries or to @code{0} to disable the data collection of this
474resolution.")
475
476 (database-synchronous
477 (maybe-integer -1)
478 "\
479Change the setting of the SQLite \"synchronous\" flag which controls how much
480care is taken to ensure disk writes have fully completed when writing data to
481the database before continuing other actions. Higher values take extra steps
482to ensure data safety at the cost of slower performance. A value of @code{0} will
483result in all handling being left to the filesystem itself. Set to @code{-1} to
484select the default value according to database mode controlled by
485@var{database-write-ahead-logging}
486setting. See SQLite documentation for more details regarding values from @code{1}
487to @code{3}. Value range: @samp{-1}..@samp{3}")
488
489 (database-write-ahead-logging?
490 (maybe-boolean #f)
491 "\
492Enable or disable SQLite Write-Ahead Logging mode for the database. See SQLite
493documentation for more details and note that support for read-only operations
494isn't available in older SQLite versions.")
495
496 (hourly-days
497 (maybe-integer 4)
498 "\
499Data retention duration for the one hour resolution entries. The configuration
500defines for how many past days entries will be stored. Set to @code{-1} for
501unlimited entries or to @code{0} to disable the data collection of this
502resolution.")
503
504 (log-file
505 maybe-string
506 "\
507Specify log file path and name to be used if @var{use-logging} is set to @code{1}.")
508
509 (max-bandwidth
510 maybe-integer
511 "\
512Maximum bandwidth for all interfaces. If the interface specific traffic
513exceeds the given value then the data is assumed to be invalid and rejected.
514Set to 0 in order to disable the feature. Value range: @samp{0}..@samp{50000}")
515
516 ;; documentation adapted for alist type
517 (max-bw
518 maybe-alist
519 "\
520Same as
521@var{max-bandwidth}
522but can be used for setting individual limits
523for selected interfaces. This is an association list of interfaces
524as strings to integer values. For example,
525@lisp
526(max-bw
527 `((\"eth0\" . 15000)
528 (\"ppp0\" . 10000)))
529@end lisp
530@var{bandwidth-detection}
531is disabled on an interface specific level for each
532@var{max-bw}
533configuration. Value range: @samp{0}..@samp{50000}"
534 (serializer
535 (lambda (field-name value)
536 (if (maybe-value-set? value)
537 (vnstat-serialize-alist field-name value) ""))))
538
539 (monthly-months
540 (maybe-integer 25)
541 "\
542Data retention duration for the one month resolution entries. The configuration
543defines for how many past months entries will be stored. Set to @code{-1} for
544unlimited entries or to @code{0} to disable the data collection of this
545resolution.")
546
547 (month-rotate
548 (maybe-integer 1)
549 "\
550Day of month that months are expected to change. Usually set to
5511 but can be set to alternative values for example for tracking
552monthly billed traffic where the billing period doesn't start on
553the first day. For example, if set to 7, days of February up to and
554including the 6th will count for January. Changing this option will
555not cause existing data to be recalculated. Value range: @samp{1}..@samp{28}")
556
557 (month-rotate-affects-years?
558 (maybe-boolean #f)
559 "\
560Enable or disable
561@var{month-rotate}
562also affecting yearly data. Applicable only when
563@var{month-rotate}
564has a value greater than one.")
565
566 (offline-save-interval
567 (maybe-integer 30)
568 "\
569How often in minutes cached interface data is saved to file when all monitored
570interfaces are offline. Value range:
571@var{save-interval}..@samp{60}")
572
573 (pid-file
574 (maybe-string "/var/run/vnstat/vnstatd.pid")
575 "\
576Specify pid file path and name to be used.")
577
578 (poll-interval
579 (maybe-integer 5)
580 "\
581How often in seconds interfaces are checked for status changes.
582Value range: @samp{2}..@samp{60}")
583
584 (rescan-database-on-save?
585 maybe-boolean
586 "\
587Automatically discover added interfaces from the database and start monitoring.
588The rescan is done every
589@var{save-interval}
590or
591@var{offline-save-interval}
592minutes depending on the current activity state.")
593
594 (save-interval
595 (maybe-integer 5)
596 "\
597How often in minutes cached interface data is saved to file.
598Value range: (
599@var{update-interval} / 60 )..@samp{60}")
600
601 (save-on-status-change?
602 (maybe-boolean #t)
603 "\
604Enable or disable the additional saving to file of cached interface data
605when the availability of an interface changes, i.e., when an interface goes
606offline or comes online.")
607
608 (time-sync-wait
609 (maybe-integer 5)
610 "\
611How many minutes to wait during daemon startup for system clock to sync if
612most recent database update appears to be in the future. This may be needed
613in systems without a real-time clock (RTC) which require some time after boot
614to query and set the correct time. @code{0} = wait disabled.
615Value range: @samp{0}..@samp{60}")
616
617 (top-day-entries
618 (maybe-integer 20)
619 "\
620Data retention duration for the top day entries. The configuration
621defines how many of the past top day entries will be stored. Set to @code{-1} for
622unlimited entries or to @code{0} to disable the data collection of this
623resolution.")
624
625 (trafficless-entries?
626 (maybe-boolean #t)
627 "\
628Create database entries even when there is no traffic during the entry's time
629period.")
630
631 (update-file-owner?
632 (maybe-boolean #t)
633 "\
634Enable or disable the update of file ownership during daemon process startup.
635During daemon startup, only database, log and pid files will be modified if the
636user or group change feature (
637@var{daemon-user}
638or
639@var{daemon-group}
640) is enabled and the files don't match the requested user or group. During manual
641database creation, this option will cause file ownership to be inherited from the
642database directory if the directory already exists. This option only has effect
643when the process is started as root or via sudo.")
644
645 (update-interval
646 (maybe-integer 20)
647 "\
648How often in seconds the interface data is updated. Value range:
649@var{poll-interval}..@samp{300}")
650
651 (use-logging
652 (maybe-integer 2)
653 "\
654Enable or disable logging. Accepted values are:
655@code{0} = disabled, @code{1} = logfile and @code{2} = syslog.")
656
657 (use-utc?
658 maybe-boolean
659 "\
660Enable or disable using UTC as timezone in the database for all entries. When
661enabled, all entries added to the database will use UTC regardless of the
662configured system timezone. When disabled, the configured system timezone
663will be used. Changing this setting will not result in already existing
664data to be modified."
665 (serializer
666 (lambda (_ value)
667 (if (maybe-value-set? value)
668 (vnstat-serialize-boolean 'use-UTC value) ""))))
669
670 (yearly-years
671 (maybe-integer -1)
672 "\
673Data retention duration for the one year resolution entries. The configuration
674defines for how many past years entries will be stored. Set to @code{-1} for
675unlimited entries or to @code{0} to disable the data collection of this
676resolution.")
677
678 (prefix vnstat-))
679
680(define (vnstat-serialize-configuration config)
681 (mixed-text-file
682 "vnstat.conf"
683 (serialize-configuration config vnstat-configuration-fields)))
684
685(define (vnstat-shepherd-service config)
686 (let ((config-file (vnstat-serialize-configuration config)))
687 (match-record config <vnstat-configuration> (package pid-file)
688 (shepherd-service
689 (documentation "Run vnstatd.")
690 (requirement `(user-processes networking file-systems))
691 (provision '(vnstatd))
692 (start #~(make-forkexec-constructor
693 (list #$(file-append package "/sbin/vnstatd")
694 "--daemon"
695 "--config" #$config-file)
696 #:pid-file #$pid-file))
697 (stop #~(make-kill-destructor))
698 (actions
699 (list (shepherd-configuration-action config-file)
700 (shepherd-signal-action
701 'reload SIGHUP
702 #:documentation "Reload vnstatd."
703 #:message "Issued SIGHUP to vnstatd.")))))))
704
705(define (vnstat-account-service config)
706 (match-record config <vnstat-configuration> (daemon-group daemon-user)
707 (filter-map maybe-value (list daemon-group daemon-user))))
708
709(define vnstat-service-type
710 (service-type
711 (name 'vnstat)
712 (description "vnStat network-traffic monitor service.")
713 (extensions
714 (list (service-extension shepherd-root-service-type
715 (compose list vnstat-shepherd-service))
716 (service-extension account-service-type
717 vnstat-account-service)))
718 (default-value (vnstat-configuration))))
719
720
721;;;
722;;; Zabbix server
723;;;
724
725(define (uglify-field-name field-name)
726 (apply string-append
727 (map (lambda (str)
728 (if (member (string->symbol str) '(ca db ssl))
729 (string-upcase str)
730 (string-capitalize str)))
731 (string-split (string-delete #\?
732 (symbol->string field-name))
733 #\-))))
734
735(define (serialize-field field-name val)
736 #~(format #f "~a=~a~%" #$(uglify-field-name field-name) #$val))
737
738(define (serialize-number field-name val)
739 (serialize-field field-name (number->string val)))
740
741(define (serialize-list field-name val)
742 #~(if (null? '#$val)
743 ""
744 #$(serialize-field field-name (string-join val ","))))
745
746
747(define (serialize-string field-name val)
748 (if (or (eq? 'user field-name)
749 (eq? 'group field-name)
750 (and (string? val) (string=? val "")))
751 ""
752 (serialize-field field-name val)))
753
754(define include-files? list?)
755
756(define (serialize-include-files field-name val)
757 #~(string-append #$@(map (cut serialize-field 'include <>) val)))
758
759(define extra-options? string?)
760
761(define (serialize-extra-options field-name val)
762 #~(if (= 0 (string-length #$val)) "" #$(format #f "~a~%" val)))
763
764(define (nginx-server-configuration-list? val)
765 (and (list? val) (and-map nginx-server-configuration? val)))
766
767(define (serialize-nginx-server-configuration-list field-name val)
768 "")
769
770(define-configuration zabbix-server-configuration
771 (zabbix-server
772 (file-like zabbix-server)
773 "The zabbix-server package.")
774 (user
775 (string "zabbix")
776 "User who will run the Zabbix server.")
777 (group
778 (string "zabbix")
779 "Group who will run the Zabbix server.")
780 (db-host
781 (string "127.0.0.1")
782 "Database host name.")
783 (db-name
784 (string "zabbix")
785 "Database name.")
786 (db-user
787 (string "zabbix")
788 "Database user.")
789 (db-password
790 (string "")
791 "Database password. Please, use @code{include-files} with
792@code{DBPassword=SECRET} inside a specified file instead.")
793 (db-port
794 (number 5432)
795 "Database port.")
796 (log-type
797 (string "")
798 "Specifies where log messages are written to:
799@itemize
800@item @code{system} - syslog.
801@item @code{file} - file specified with @code{log-file} parameter.
802@item @code{console} - standard output.
803@end itemize\n")
804 (log-file
805 (string "/var/log/zabbix/server.log")
806 "Log file name for @code{log-type} @code{file} parameter.")
807 (pid-file
808 (string "/var/run/zabbix/zabbix_server.pid")
809 "Name of PID file.")
810 (ssl-ca-location
811 (string "/etc/ssl/certs/ca-certificates.crt")
812 "The location of certificate authority (CA) files for SSL server
813certificate verification.")
814 (ssl-cert-location
815 (string "/etc/ssl/certs")
816 "Location of SSL client certificates.")
817 (extra-options
818 (extra-options "")
819 "Extra options will be appended to Zabbix server configuration file.")
820 (include-files
821 (include-files '())
822 "You may include individual files or all files in a directory in the
823configuration file."))
824
825(define (zabbix-server-account config)
826 "Return the user accounts and user groups for CONFIG."
827 (let ((zabbix-user (zabbix-server-configuration-user config))
828 (zabbix-group (zabbix-server-configuration-group config)))
829 (list (user-group (name zabbix-group) (system? #t))
830 (user-account
831 (name zabbix-user)
832 (system? #t)
833 (group zabbix-group)
834 (comment "zabbix privilege separation user")
835 (home-directory (string-append "/var/run/" zabbix-user))
836 (shell (file-append shadow "/sbin/nologin"))))))
837
838(define (zabbix-server-config-file config)
839 "Return the zabbix-server configuration file corresponding to CONFIG."
840 (computed-file
841 "zabbix_server.conf"
842 #~(begin
843 (call-with-output-file #$output
844 (lambda (port)
845 (format port "# Generated by 'zabbix-server-service'.~%")
846 (format port #$(serialize-configuration
847 config zabbix-server-configuration-fields)))))))
848
849(define (zabbix-server-activation config)
850 "Return the activation gexp for CONFIG."
851 (with-imported-modules '((guix build utils))
852 #~(begin
853 (use-modules (guix build utils)
854 (ice-9 rdelim))
855 (let ((user (getpw #$(zabbix-server-configuration-user config))))
856 (for-each (lambda (file)
857 (let ((directory (dirname file)))
858 (mkdir-p directory)
859 (chown directory (passwd:uid user) (passwd:gid user))
860 (chmod directory #o755)))
861 (list #$(zabbix-server-configuration-log-file config)
862 #$(zabbix-server-configuration-pid-file config)
863 "/etc/zabbix/maintenance.inc.php"))))))
864
865(define (zabbix-server-runtime-control-procedure zabbix-server config command)
866 ;; XXX: This is duplicated from mcron; factorize.
867 #~(lambda (_ . args)
868 ;; Run 'zabbix_server' in a pipe so we can explicitly redirect its output
869 ;; to 'current-output-port', which at this stage is bound to the client
870 ;; connection.
871 (let ((pipe (apply open-pipe* OPEN_READ #$zabbix-server
872 "--config" #$config
873 "-R" #$command args)))
874 (let loop ()
875 (match (read-line pipe 'concat)
876 ((? eof-object?)
877 (catch 'system-error
878 (lambda ()
879 (zero? (close-pipe pipe)))
880 (lambda args
881 ;; There's a race with the SIGCHLD handler, which could
882 ;; call 'waitpid' before 'close-pipe' above does. If we
883 ;; get ECHILD, that means we lost the race; in that case, we
884 ;; cannot tell what the exit code was (FIXME).
885 (or (= ECHILD (system-error-errno args))
886 (apply throw args)))))
887 (line
888 (display line)
889 (loop)))))))
890
891;; Provide shepherd actions for common "zabbix_server -R" commands
892;; mainly for a convenient way to use the correct configuration file.
893(define (zabbix-server-actions zabbix-server config)
894 (list (shepherd-action
895 (name 'reload-config-cache)
896 (documentation "Reload the configuration cache.")
897 (procedure (zabbix-server-runtime-control-procedure
898 zabbix-server config "config_cache_reload")))
899 (shepherd-action
900 (name 'reload-snmp-cache)
901 (documentation "Reload SNMP cache.")
902 (procedure (zabbix-server-runtime-control-procedure
903 zabbix-server config "snmp_cache_reload")))))
904
905(define (zabbix-server-shepherd-service config)
906 "Return a <shepherd-service> for Zabbix server with CONFIG."
907 (let ((zabbix-server
908 (file-append (zabbix-server-configuration-zabbix-server config)
909 "/sbin/zabbix_server"))
910 (config-file (zabbix-server-config-file config)))
911 (list (shepherd-service
912 (provision '(zabbix-server))
913 (requirement '(user-processes))
914 (documentation "Run the Zabbix server daemon.")
915 (actions (zabbix-server-actions zabbix-server config-file))
916 (start #~(make-forkexec-constructor
917 (list #$zabbix-server
918 "--config" #$config-file
919 "--foreground")
920 #:user #$(zabbix-server-configuration-user config)
921 #:group #$(zabbix-server-configuration-group config)
922 #:pid-file #$(zabbix-server-configuration-pid-file config)
923 #:environment-variables
924 (list "SSL_CERT_DIR=/run/current-system/profile\
925/etc/ssl/certs"
926 "SSL_CERT_FILE=/run/current-system/profile\
927/etc/ssl/certs/ca-certificates.crt")))
928 (stop #~(make-kill-destructor
929 ;; The server needs to finish database work on shutdown
930 ;; which can take a while for big or busy databases.
931 #:grace-period 60))))))
932
933(define zabbix-server-service-type
934 (service-type
935 (name 'zabbix-server)
936 (extensions
937 (list (service-extension shepherd-root-service-type
938 zabbix-server-shepherd-service)
939 (service-extension account-service-type
940 zabbix-server-account)
941 (service-extension activation-service-type
942 zabbix-server-activation)))
943 (default-value (zabbix-server-configuration))
944 (description "Run the Zabbix server, a high-performance monitoring system
945that can collect data about machines from a variety of sources and provide the
946results in a Web interface.")))
947
948(define (generate-zabbix-server-documentation)
949 (generate-documentation
950 `((zabbix-server-configuration
951 ,zabbix-server-configuration-fields))
952 'zabbix-server-configuration))
953
954(define-configuration zabbix-agent-configuration
955 (zabbix-agent
956 (file-like zabbix-agentd)
957 "The zabbix-agent package.")
958 (user
959 (string "zabbix")
960 "User who will run the Zabbix agent.")
961 (group
962 (string "zabbix")
963 "Group who will run the Zabbix agent.")
964 (hostname
965 (string "")
966 "Unique, case sensitive hostname which is required for active checks and
967must match hostname as configured on the server.")
968 (log-type
969 (string "")
970 "Specifies where log messages are written to:
971@itemize
972@item @code{system} - syslog.
973@item @code{file} - file specified with @code{log-file} parameter.
974@item @code{console} - standard output.
975@end itemize\n")
976 (log-file
977 (string "/var/log/zabbix/agent.log")
978 "Log file name for @code{log-type} @code{file} parameter.")
979 (pid-file
980 (string "/var/run/zabbix/zabbix_agent.pid")
981 "Name of PID file.")
982 (server
983 (list '("127.0.0.1"))
984 "List of IP addresses, optionally in CIDR notation, or hostnames of Zabbix
985servers and Zabbix proxies. Incoming connections will be accepted only from
986the hosts listed here.")
987 (server-active
988 (list '("127.0.0.1"))
989 "List of IP:port (or hostname:port) pairs of Zabbix servers and Zabbix
990proxies for active checks. If port is not specified, default port is used.
991If this parameter is not specified, active checks are disabled.")
992 (extra-options
993 (extra-options "")
994 "Extra options will be appended to Zabbix server configuration file.")
995 (include-files
996 (include-files '())
997 "You may include individual files or all files in a directory in the
998configuration file."))
999
1000(define (zabbix-agent-account config)
1001 "Return the user accounts and user groups for CONFIG."
1002 (let ((zabbix-user (zabbix-agent-configuration-user config))
1003 (zabbix-group (zabbix-agent-configuration-group config)))
1004 (list (user-group (name zabbix-group) (system? #t))
1005 (user-account
1006 (name zabbix-user)
1007 (system? #t)
1008 (group zabbix-group)
1009 (comment "zabbix privilege separation user")
1010 (home-directory (string-append "/var/run/" zabbix-user))
1011 (shell (file-append shadow "/sbin/nologin"))))))
1012
1013(define (zabbix-agent-activation config)
1014 "Return the activation gexp for CONFIG."
1015 (with-imported-modules '((guix build utils))
1016 #~(begin
1017 (use-modules (guix build utils)
1018 (ice-9 rdelim))
1019 (let ((user
1020 (getpw #$(zabbix-agent-configuration-user config))))
1021 (for-each (lambda (file)
1022 (let ((directory (dirname file)))
1023 (mkdir-p directory)
1024 (chown directory (passwd:uid user) (passwd:gid user))
1025 (chmod directory #o755)))
1026 (list #$(zabbix-agent-configuration-log-file config)
1027 #$(zabbix-agent-configuration-pid-file config)))))))
1028
1029(define (zabbix-agent-config-file config)
1030 "Return the zabbix-agent configuration file corresponding to CONFIG."
1031 (computed-file
1032 "zabbix_agent.conf"
1033 #~(begin
1034 (call-with-output-file #$output
1035 (lambda (port)
1036 (format port "# Generated by 'zabbix-agent-service'.~%")
1037 (format port #$(serialize-configuration
1038 config zabbix-agent-configuration-fields)))))))
1039
1040(define (zabbix-agent-arguments config)
1041 #~(let* ((config-file #$(zabbix-agent-config-file config))
1042 (agent #$(zabbix-agent-configuration-zabbix-agent config))
1043 (agent2? (file-exists? (string-append agent "/sbin/zabbix_agent2"))))
1044 (if agent2?
1045 (list (string-append agent "/sbin/zabbix_agent2")
1046 "-config" config-file
1047 "-foreground")
1048 (list (string-append agent "/sbin/zabbix_agentd")
1049 "--config" config-file
1050 "--foreground"))))
1051
1052(define (zabbix-agent-shepherd-service config)
1053 "Return a <shepherd-service> for Zabbix agent with CONFIG."
1054 (list (shepherd-service
1055 (provision '(zabbix-agent))
1056 (requirement '(user-processes))
1057 (documentation "Run Zabbix agent daemon.")
1058 (start #~(make-forkexec-constructor
1059 #$(zabbix-agent-arguments config)
1060 #:user #$(zabbix-agent-configuration-user config)
1061 #:group #$(zabbix-agent-configuration-group config)
1062 #:pid-file #$(zabbix-agent-configuration-pid-file config)
1063 #:environment-variables
1064 (list "SSL_CERT_DIR=/run/current-system/profile\
1065/etc/ssl/certs"
1066 "SSL_CERT_FILE=/run/current-system/profile\
1067/etc/ssl/certs/ca-certificates.crt"
1068 "PATH=/run/privileged/bin:\
1069/run/current-system/profile/bin:/run/current-system/profile/sbin")))
1070 (stop #~(make-kill-destructor)))))
1071
1072(define zabbix-agent-service-type
1073 (service-type
1074 (name 'zabbix-agent)
1075 (extensions
1076 (list (service-extension shepherd-root-service-type
1077 zabbix-agent-shepherd-service)
1078 (service-extension account-service-type
1079 zabbix-agent-account)
1080 (service-extension activation-service-type
1081 zabbix-agent-activation)))
1082 (default-value (zabbix-agent-configuration))
1083 (description "Run the Zabbix agent, @command{zabbix_agentd}, which gathers
1084information about the running system for the Zabbix monitoring server.")))
1085
1086(define (generate-zabbix-agent-documentation)
1087 (generate-documentation
1088 `((zabbix-agent-configuration
1089 ,zabbix-agent-configuration-fields))
1090 'zabbix-agent-configuration))
1091
1092(define %zabbix-front-end-configuration-nginx
1093 (nginx-server-configuration
1094 (root #~(string-append #$zabbix-server:front-end "/share/zabbix/php"))
1095 (index '("index.php"))
1096 (locations
1097 (let ((php-location (nginx-php-location)))
1098 (list (nginx-location-configuration
1099 (inherit php-location)
1100 (body (append (nginx-location-configuration-body php-location)
1101 (list "
1102fastcgi_param PHP_VALUE \"post_max_size = 16M
1103 max_execution_time = 300\";
1104")))))))
1105 (listen '("80"))))
1106
1107(define (zabbix-front-end-nginx-extension config)
1108 (match config
1109 (($ <zabbix-front-end-configuration> server nginx)
1110 (if (null? nginx)
1111 (list
1112 (nginx-server-configuration
1113 (inherit %zabbix-front-end-configuration-nginx)
1114 (root #~(string-append #$server:front-end "/share/zabbix/php"))))
1115 nginx))))
1116
1117(define-configuration zabbix-front-end-configuration
1118 (zabbix-server
1119 (file-like zabbix-server)
1120 "The Zabbix server package to use.")
1121 (nginx
1122 (list '())
1123 "List of @ref{nginx-server-configuration,@code{nginx-server-configuration}}
1124blocks for the Zabbix front-end. When empty, a default that listens on port 80
1125is used.")
1126 (db-host
1127 (string "localhost")
1128 "Database host name.")
1129 (db-port
1130 (number 5432)
1131 "Database port.")
1132 (db-name
1133 (string "zabbix")
1134 "Database name.")
1135 (db-user
1136 (string "zabbix")
1137 "Database user.")
1138 (db-password
1139 (string "")
1140 "Database password. Please, use @code{db-secret-file} instead.")
1141 (db-secret-file
1142 (string "")
1143 "Secret file which will be appended to @file{zabbix.conf.php} file. This
1144file contains credentials for use by Zabbix front-end. You are expected to
1145create it manually.")
1146 (zabbix-host
1147 (string "localhost")
1148 "Zabbix server hostname.")
1149 (zabbix-port
1150 (number 10051)
1151 "Zabbix server port."))
1152
1153(define (zabbix-front-end-config config)
1154 (match-record config <zabbix-front-end-configuration>
1155 (db-host db-port db-name db-user db-password db-secret-file
1156 zabbix-host zabbix-port %location)
1157 (mixed-text-file "zabbix.conf.php"
1158 "\
1159<?php
1160// Zabbix GUI configuration file.
1161global $DB;
1162
1163$DB['TYPE'] = 'POSTGRESQL';
1164$DB['SERVER'] = '" db-host "';
1165$DB['PORT'] = '" (number->string db-port) "';
1166$DB['DATABASE'] = '" db-name "';
1167$DB['USER'] = '" db-user "';
1168$DB['PASSWORD'] = " (let ((file (location-file %location))
1169 (line (location-line %location))
1170 (column (location-column %location)))
1171 (if (string-null? db-password)
1172 (if (string-null? db-secret-file)
1173 (raise (make-compound-condition
1174 (condition
1175 (&message
1176 (message
1177 (format #f "no '~A' or '~A' field in your '~A' record"
1178 'db-secret-file 'db-password
1179 'zabbix-front-end-configuration))))
1180 (condition
1181 (&error-location
1182 (location %location)))))
1183 (string-append "trim(file_get_contents('"
1184 db-secret-file "'));\n"))
1185 (begin
1186 (display-hint (G_ "~a:~a:~a: ~a:
1187Consider using @code{db-secret-file} instead of @code{db-password} for better
1188security.")
1189 file line column
1190 'zabbix-front-end-configuration)
1191 (format #f "'~a';~%" db-password))))
1192 "
1193// Schema name. Used for IBM DB2 and PostgreSQL.
1194$DB['SCHEMA'] = '';
1195
1196// Use IEEE754 compatible value range for 64-bit Numeric (float) history values.
1197$DB['DOUBLE_IEEE754'] = true;
1198
1199$ZBX_SERVER = '" zabbix-host "';
1200$ZBX_SERVER_PORT = '" (number->string zabbix-port) "';
1201$ZBX_SERVER_NAME = '';
1202
1203$IMAGE_FORMAT_DEFAULT = IMAGE_FORMAT_PNG;
1204")))
1205
1206(define %maintenance.inc.php
1207 ;; Empty php file to allow us move zabbix-frontend configs to ‘/etc/zabbix’
1208 ;; directory. See ‘install-front-end’ phase in
1209 ;; (@ (gnu packages monitoring) zabbix-server) package.
1210 "\
1211<?php
1212")
1213
1214(define (zabbix-front-end-activation config)
1215 "Return the activation gexp for CONFIG."
1216 #~(begin
1217 (use-modules (guix build utils))
1218 (mkdir-p "/etc/zabbix")
1219 (call-with-output-file "/etc/zabbix/maintenance.inc.php"
1220 (lambda (port)
1221 (display #$%maintenance.inc.php port)))
1222 (copy-file #$(zabbix-front-end-config config)
1223 "/etc/zabbix/zabbix.conf.php")))
1224
1225(define zabbix-front-end-service-type
1226 (service-type
1227 (name 'zabbix-front-end)
1228 (extensions
1229 (list (service-extension activation-service-type
1230 zabbix-front-end-activation)
1231 (service-extension nginx-service-type
1232 zabbix-front-end-nginx-extension)
1233 ;; Make sure php-fpm is instantiated.
1234 (service-extension php-fpm-service-type
1235 (const #t))))
1236 (default-value (zabbix-front-end-configuration))
1237 (description
1238 "Run the zabbix-front-end web interface, which allows users to interact
1239with Zabbix server.")))
1240
1241(define (generate-zabbix-front-end-documentation)
1242 (generate-documentation
1243 `((zabbix-front-end-configuration
1244 ,zabbix-front-end-configuration-fields))
1245 'zabbix-front-end-configuration))
1246
1247
1248;;
1249;; collectd
1250;;
1251
1252(define *indent* (make-parameter ""))
1253
1254(defmacro with-indent (. body)
1255 `(parameterize ((*indent* (string-append (*indent*) " ")))
1256 ,@body))
1257
1258(define seconds? integer?)
1259
1260(define-maybe/no-serialization seconds)
1261
1262(define list-of-string? (list-of string?))
1263
1264(define list-of-file-likes? (list-of file-like?))
1265
1266(define (collectd-serialize-list-of-file-likes name value)
1267 #~(string-join
1268 (map
1269 (lambda (v) (format #f "~a~a \"~a\"" #$name #$(*indent*) v))
1270 '#$value)
1271 "\n" 'suffix))
1272
1273(define (collectd-serialize-file-like name value)
1274 (collectd-serialize-list-of-file-likes name (list value)))
1275
1276(define list-of-packages? (list-of package?))
1277
1278(define (collectd-serialize-value value)
1279 (cond
1280 ;; Strings get quoted and escaped.
1281 ((string? value)
1282 (format #f "~s" value))
1283
1284 ;; Keywords become unquoted strings (#:foo -> foo).
1285 ((keyword? value)
1286 (collectd-serialize-value
1287 (keyword->symbol value)))
1288
1289 ;; Booleans become bare words.
1290 ((eq? value #t) "true")
1291 ((eq? value #f) "false")
1292
1293 ;; Alists serialize to lines of KEY VALUE.
1294 ((alist? value)
1295 (apply string-append
1296 (map
1297 (generic-serialize-alist-entry collectd-serialize-field)
1298 value)))
1299
1300 ;; Lists get their elements serialized and joined with a space.
1301 ((list? value) (string-join (map collectd-serialize-value value) " "))
1302
1303 ;; Other types (numbers etc) turn into bare strings.
1304 (else (object->string value))))
1305
1306(define (collectd-serialize-field name value)
1307 (if (and (list? value) (null-list? value))
1308 ""
1309 (format #nil "~a~a ~a~%" (*indent*) name
1310 (collectd-serialize-value value))))
1311
1312(define (remap-names name-map serializer)
1313 "Renaming serializer.
1314
1315Wraps `serializer', renaming fields according to `name-map', an alist of
1316'((field-name . serialized-name))."
1317 (lambda (name value)
1318 (serializer (or (and=> (assoc name name-map) cdr) name) value)))
1319
1320(define (collectd-make-load-plugin?-serializer plugin-name)
1321 (lambda (_ load-plugin?)
1322 (if load-plugin?
1323 (collectd-serialize-field "LoadPlugin" plugin-name)
1324 "")))
1325
1326;; Generic plugin support.
1327
1328(define-configuration/no-serialization collectd-plugin-generic
1329 (load-plugin?
1330 (boolean #t)
1331 "When @code{#t}, include a @code{LoadPlugin} directive in the
1332configuration. This interacts with @code{auto-load-plugins?} in
1333@code{collectd-configuration}; if @code{auto-load-plugins?} is @code{#f}, all
1334plugins should set this to @code{#t}.")
1335
1336 (name
1337 string
1338 "The name of the plugin to configure. See @code{collect.conf(5)} for the
1339list of plugins.")
1340
1341 (options
1342 (alist '())
1343 "Configuration options for the plugin, as an alist. See
1344@code{collect.conf(5)} for the available plugin options."))
1345
1346(define (collectd-serialize-plugin-generic _ value)
1347 (match-record value <collectd-plugin-generic>
1348 (name load-plugin? options)
1349 #~(string-join
1350 `("\n"
1351 ,#$((collectd-make-load-plugin?-serializer name) #nil load-plugin?)
1352 ,(string-append "<Plugin " #$name ">\n")
1353 ,#$(with-indent (collectd-serialize-value options))
1354 "</Plugin>\n")
1355 "")))
1356
1357;; Python plugin support.
1358
1359(define (collectd-serialize-python-module name value)
1360 (apply string-append
1361 `(,(string-append (*indent*) "<Module "
1362 (collectd-serialize-value name)
1363 ">")
1364 "\n"
1365 ,@(with-indent
1366 (map
1367 (lambda (kvs)
1368 (collectd-serialize-field (car kvs) (cdr kvs)))
1369 value))
1370 ,(string-append (*indent*) "</Module>"))))
1371
1372(define collectd-plugin-python-remap
1373 '((module-paths . "ModulePath")
1374 (load-plugin? . "LoadPlugin")
1375 (log-traces? . "LogTraces")
1376 (interactive? . "Interactive")
1377 (type-databases . "TypesDB")
1378 (packages . "ModulePath")
1379 (module . "Import")))
1380
1381(define collectd-serialize-plugin-python-field
1382 (remap-names collectd-plugin-python-remap
1383 collectd-serialize-field))
1384
1385(define-configuration collectd-plugin-python
1386 (load-plugin?
1387 (boolean #t)
1388 "When @code{#t}, include a @code{LoadPlugin} directive in the
1389configuration. This interacts with @code{auto-load-plugins?} in
1390@code{collectd-configuration}; if @code{collectd-configuration}'s
1391@code{auto-load-plugins?} is @code{#f}, all plugins should set this to
1392@code{#t}."
1393 (serializer (collectd-make-load-plugin?-serializer "python")))
1394
1395 (type-databases
1396 (list-of-file-likes '())
1397 "One or more files that contain the data-set descriptions. See
1398@code{types.db(5)} for a description of the format of these files."
1399 (serializer (remap-names collectd-plugin-python-remap
1400 collectd-serialize-list-of-file-likes)))
1401
1402 (packages
1403 (list-of-packages '())
1404 "Packages to make available to the Python plugin. These can be
1405dependencies of the plugin code, or may contain the plugin."
1406 (serializer empty-serializer))
1407
1408 (module-paths
1409 (list-of-string '())
1410 "Entries to prepend to @code{sys.path}."
1411 (serializer collectd-serialize-plugin-python-field))
1412
1413 (log-traces?
1414 (boolean #f)
1415 "If a Python script throws an exception it will be logged by
1416collectd with the name of the exception and the message. If you set
1417this option to true it will also log the full stacktrace just like the
1418default output of an interactive Python interpreter. This does not
1419apply to the @code{CollectError} exception, which will never log a
1420stacktrace. This should probably be set to false most of the time but
1421is very useful for development and debugging of new modules."
1422 (serializer collectd-serialize-plugin-python-field))
1423
1424 (interactive?
1425 (boolean #f)
1426 "This option will cause the module to launch an interactive Python
1427interpreter that reads from and writes to the terminal. Note that collectd
1428will terminate right after starting up if you try to run it as a daemon while
1429this option is enabled so make sure to start collectd with the @code{-f}
1430option. See the @code{collectd-python(5)} man page for more information on
1431this option."
1432 (serializer collectd-serialize-plugin-python-field))
1433
1434 (module
1435 string
1436 "The name of the Python module to import into the collectd Python
1437process. The module must be available in @code{packages} or
1438@code{module-paths}, and register a MPD callback."
1439 (serializer collectd-serialize-plugin-python-field))
1440
1441 (module-options
1442 (alist '())
1443 "Configuration options for the module."
1444 (serializer (lambda (_ value) (collectd-serialize-value value)))))
1445
1446(define (collectd-serialize-plugin-python _ value)
1447 (define (serialize-fields fields)
1448 (list-transduce
1449 (base-transducer value) rcons
1450 (filter-configuration-fields collectd-plugin-python-fields fields)))
1451
1452 (match-record
1453 value <collectd-plugin-python> (module)
1454 #~(string-append
1455 "\n"
1456 #$@(serialize-fields '(load-plugin? type-databases))
1457 "<Plugin python>\n"
1458 #$@(with-indent
1459 (append
1460 (serialize-fields '(log-traces? interactive? module-paths module))
1461 (list (*indent*) "<Module " (collectd-serialize-value module) ">\n")
1462 (with-indent (serialize-fields '(module-options)))
1463 (list (*indent*) "</Module>\n")))
1464 "</Plugin>\n")))
1465
1466(define (collectd-plugin? x)
1467 ;; XXX: Extend this if plugin-specific configuration records are
1468 ;; added.
1469 (or (collectd-plugin-generic? x)
1470 (collectd-plugin-python? x)))
1471
1472(define list-of-collectd-plugins? (list-of collectd-plugin?))
1473
1474(define (collectd-serialize-plugin name value)
1475 ((match value
1476 ;; XXX: Extend this if plugin-specific configuration records are
1477 ;; added.
1478 ;; Note that these *must* return gexps, not strings, otherwise things
1479 ;; like type-databases won't work.
1480 (($ <collectd-plugin-generic>) collectd-serialize-plugin-generic)
1481 (($ <collectd-plugin-python>) collectd-serialize-plugin-python))
1482 name value))
1483
1484(define (collectd-serialize-list-of-plugins name value)
1485 #~(string-append
1486 #$@(map
1487 (lambda (v) (collectd-serialize-plugin name v))
1488 value)))
1489
1490(define collectd-configuration-remap
1491 '((base-directory . "BaseDir")
1492 (auto-load-plugins? . "AutoLoadPlugin")
1493 (collect-internal-stats? . "CollectInternalStats")
1494 (type-databases . "TypesDB")
1495 (interval . "Interval")
1496 (max-read-interval . "MaxReadInterval")
1497 (timeout . "Timeout")
1498 (read-threads . "ReadThreads")
1499 (write-threads . "WriteThreads")
1500 (write-queue-limit-high . "WriteQueueLimitHigh")
1501 (write-queue-limit-low . "WriteQueueLimitLow")
1502 (host-name . "Hostname")
1503 (fully-qualified-domain-name-lookup? . "FQDNLookup")))
1504
1505(define collectd-configuration-serialize-field
1506 (remap-names collectd-configuration-remap
1507 collectd-serialize-field))
1508
1509(define %collectd-default-type-database
1510 (file-append collectd "/share/collectd/types.db"))
1511
1512(define-configuration collectd-configuration
1513 (collectd
1514 (package collectd)
1515 "The collectd package to use."
1516 (serializer empty-serializer))
1517
1518 (base-directory
1519 (string "/var/lib/collectd")
1520 "Sets the base directory. This is the directory beneath which all
1521@acronym{RRD, round-robin database} files are created. Possibly more
1522subdirectories are created. This is also the working directory for
1523collectd."
1524 (serializer collectd-configuration-serialize-field))
1525
1526 (auto-load-plugins?
1527 (boolean #f)
1528 "When set to @code{#f}, plugins must be loaded explicitly, by setting the
1529@code{load-plugin?} field of each plugin configuration to @code{#t}. If a
1530@code{<Plugin ...>} block is encountered and no configuration handling
1531callback for this plugin has been registered, a warning is logged and the
1532block is ignored.
1533
1534When set to @code{#t}, each @code{<Plugin ...>} block acts as if it was
1535immediately preceded by a @code{LoadPlugin} statement. @code{LoadPlugin}
1536statements are still required for plugins that don't provide any
1537configuration, e.g. the @code{Load} plugin."
1538 (serializer collectd-configuration-serialize-field))
1539
1540 (collect-internal-stats?
1541 (boolean #f)
1542 "When @code{#t}, various statistics about the collectd daemon will
1543be collected, with \"collectd\" as the plugin name."
1544 (serializer collectd-configuration-serialize-field))
1545
1546 (type-databases
1547 (list-of-file-likes (list %collectd-default-type-database))
1548 "One or more files that contain the data-set descriptions. See
1549@code{types.db(5)} for a description of the format of these files."
1550 (serializer (remap-names collectd-configuration-remap
1551 collectd-serialize-list-of-file-likes)))
1552
1553 (interval
1554 (seconds 60)
1555 "Configures the interval in which to query the read plugins.
1556Smaller values lead to a higher system load produced by
1557collectd, while higher values lead to more coarse statistics.
1558
1559Warning: You should set this once and then never touch it again. If
1560you do, you will have to delete all your RRD files or know some
1561serious RRDtool magic! (Assuming you're using the RRDtool or
1562RRDCacheD plugin.)"
1563 (serializer collectd-configuration-serialize-field))
1564
1565 (max-read-interval
1566 (seconds 86400)
1567 "A read plugin doubles the interval between queries after each
1568failed attempt to get data.
1569
1570This options limits the maximum value of the interval."
1571 (serializer collectd-configuration-serialize-field))
1572
1573 (timeout
1574 maybe-integer
1575 "Consider a value list \"missing\" when no update has been read or
1576received for @code{Iterations} iterations. By default, collectd considers
1577a value list missing when no update has been received for twice the
1578update interval. Since this setting uses iterations, the maximum
1579allowed time without update depends on the @code{Interval} information
1580contained in each value list. This is used in the @code{Threshold}
1581configuration to dispatch notifications about missing values, see
1582@code{collectd-threshold(5)} for details."
1583 (serializer collectd-configuration-serialize-field))
1584
1585 (read-threads
1586 (integer 5)
1587 "Number of threads to start for reading plugins. You may want to
1588increase this if you have more than five plugins that take a long time
1589to read. Mostly those are plugins that do network-IO. Setting this to
1590a value higher than the number of registered read callbacks is not
1591recommended."
1592 (serializer collectd-configuration-serialize-field))
1593
1594 (write-threads
1595 (integer 5)
1596 "Number of threads to start for dispatching value lists to write
1597plugins. You may want to increase this if you have more than five
1598plugins that may take relatively long to write to."
1599 (serializer collectd-configuration-serialize-field))
1600
1601 (write-queue-limit-high
1602 maybe-integer
1603 "Metrics are read by the read threads and then put into a queue to
1604be handled by the write threads. If one of the write plugins is
1605slow (e.g. network timeouts, I/O saturation of the disk) this queue
1606will grow. In order to avoid running into memory issues in such a
1607case, you can limit the size of this queue.
1608
1609If there are @code{write-queue-limit-high} metrics in the queue, any
1610new metrics will be dropped.
1611
1612If the number of metrics currently in the queue is between
1613@code{write-queue-limit-low} and @code{write-queue-limit-high}, the
1614metric is dropped with a probability that is proportional to the
1615number of metrics in the queue (i.e. it increases linearly until it
1616reaches 100%)."
1617 (serializer collectd-configuration-serialize-field))
1618
1619 (write-queue-limit-low
1620 maybe-integer
1621 "If there are less than @code{write-queue-limit-low} metrics in the
1622queue, all new metrics will be enqueued.
1623
1624If @code{write-queue-limit-high} is set to non-zero and
1625@code{write-queue-limit-low} is unset, the latter will default to half
1626of @code{write-queue-limit-high}."
1627 (serializer collectd-configuration-serialize-field))
1628
1629 (host-name
1630 maybe-string
1631 "Sets the hostname that identifies a host. If you omit this setting,
1632the hostname will be determined using the @code{gethostname(2)} system
1633call."
1634 (serializer collectd-configuration-serialize-field))
1635
1636 (fully-qualified-domain-name-lookup?
1637 (boolean #t)
1638 "If @code{host-name} is determined automatically, this setting
1639controls whether or not the daemon should try to figure out the
1640@acronym{FQDN, fully qualified domain name}. This is done using a
1641lookup of the name returned by @code{gethostname(2)}."
1642 (serializer collectd-configuration-serialize-field))
1643
1644 (plugins
1645 (list-of-collectd-plugins '())
1646 "Configurations for collectd plugins."
1647 (serializer collectd-serialize-list-of-plugins))
1648
1649 (prefix collectd-))
1650
1651(define (collectd-configuration-python config)
1652 "Return the Python package collectd was built with, or #f."
1653 (and=> (assoc-ref (package-native-inputs
1654 (collectd-configuration-collectd config))
1655 "python")
1656 car))
1657
1658(define (collectd-python-packages config)
1659 "Return a list of all packages in Python plugins."
1660 (append-map
1661 (lambda (plugin)
1662 (if (collectd-plugin-python? plugin)
1663 (collectd-plugin-python-packages plugin)
1664 '()))
1665 (collectd-configuration-plugins config)))
1666
1667(define (collectd-profile-packages config)
1668 "Return packages to include in the profile for collectd."
1669 (let ((python (collectd-configuration-python config))
1670 (python-packages (collectd-python-packages config)))
1671 (cons
1672 (collectd-configuration-collectd config)
1673 ;; Only include Python and packages if collectd has support.
1674 (if python
1675 (cons python python-packages)
1676 '()))))
1677
1678(define (make-collectd-profile-wrapper config)
1679 "Return a gexp representing a wrapper for collectd, which runs it in a profile."
1680 (let* ((collectd (collectd-configuration-collectd config))
1681 (packages (collectd-profile-packages config))
1682 (collectd-profile (profile
1683 (content
1684 (packages->manifest packages)))))
1685 (computed-file
1686 "collectd-profile-wrapper"
1687 (with-imported-modules '((guix build utils))
1688 #~(begin
1689 (use-modules (guix build utils))
1690 (call-with-output-file #$output
1691 (lambda (output)
1692 (format output
1693 (string-append
1694 "#! ~a~%~%"
1695 ". ~a~%"
1696 "exec -a \"${0##*/}\" ~a \"$@\"~%")
1697 #$(file-append bash "/bin/bash")
1698 #$(file-append collectd-profile "/etc/profile")
1699 #$(file-append collectd "/sbin/collectd"))))
1700 (chmod #$output #o755))))))
1701
1702(define %collectd-pid-file "/var/run/collectd.pid")
1703
1704(define (make-collectd-shepherd-service config)
1705 (let ((config-file
1706 (mixed-text-file
1707 "collectd.conf"
1708 (serialize-configuration config collectd-configuration-fields))))
1709 (match-record config <collectd-configuration> (collectd)
1710 (shepherd-service
1711 (provision '(collectd))
1712 (documentation "Run collectd.")
1713 (requirement '(user-processes networking))
1714 (start
1715 #~(make-forkexec-constructor
1716 (list #$(make-collectd-profile-wrapper config)
1717 "-C" #$config-file
1718 "-B" ; Don't create base dir.
1719 "-P" #$%collectd-pid-file)
1720 #:pid-file #$%collectd-pid-file))
1721 (stop #~(make-kill-destructor))))))
1722
1723(define (collectd-activation config)
1724 (match-record config <collectd-configuration> (base-directory)
1725 (with-imported-modules
1726 (source-module-closure '((guix build utils)))
1727 #~(begin
1728 (use-modules (guix build utils))
1729 (mkdir-p #$base-directory)))))
1730
1731(define collectd-service-type
1732 (service-type
1733 (name 'collectd)
1734 (description "Run collectd")
1735 (extensions
1736 (list
1737 (service-extension shepherd-root-service-type
1738 (compose list make-collectd-shepherd-service))
1739 (service-extension activation-service-type
1740 collectd-activation)))
1741 (compose concatenate)
1742 (extend
1743 (lambda (config plugins)
1744 (collectd-configuration
1745 (inherit config)
1746 (plugins (append (collectd-configuration-plugins config) plugins)))))
1747 (default-value (collectd-configuration))))