all messages for Guix-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* [bug#60788] [PATCH] services: Add vnstat-service-type.
@ 2023-01-13 20:07 Bruno Victal
  2023-01-14 21:06 ` [bug#60788] [PATCH v2] " Bruno Victal
                   ` (13 more replies)
  0 siblings, 14 replies; 27+ messages in thread
From: Bruno Victal @ 2023-01-13 20:07 UTC (permalink / raw)
  To: 60788; +Cc: Bruno Victal

* gnu/services/monitoring.scm (vnstat-service-type): New variable.
* doc/guix.texi (Monitoring Services): Document it.
---
 doc/guix.texi               | 238 +++++++++++++++++++++
 gnu/services/monitoring.scm | 413 ++++++++++++++++++++++++++++++++++++
 2 files changed, 651 insertions(+)

diff --git a/doc/guix.texi b/doc/guix.texi
index 751d0957d8..b51a488a85 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -111,6 +111,7 @@
 Copyright @copyright{} 2022 John Kehayias@*
 Copyright @copyright{} 2022 Ivan Vilata-i-Balaguer@*
 Copyright @copyright{} 2023 Giacomo Leidi@*
+Copyright @copyright{} 2023 Bruno Victal@*
 
 Permission is granted to copy, distribute and/or modify this document
 under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -28219,6 +28220,243 @@ Monitoring Services
 @end table
 @end deftp
 
+@anchor{vnstat}
+@subsubheading vnStat Network Traffic Monitor
+@cindex vnstat
+
+vnStat is a network traffic monitor that uses interface statistics provided
+by the kernel rather than traffic sniffing.  This makes it a light resource
+monitor, regardless of network traffic rate.
+
+@defvar vnstat-service-type
+This is the service type for the @uref{https://humdi.net/vnstat/,vnStat} daemon
+and accepts a @code{vnstat-configuration} value.
+@end defvar
+
+@c %start of fragment
+@deftp {Data Type} vnstat-configuration
+Available @code{vnstat-configuration} fields are:
+
+@table @asis
+@item @code{package} (default: @code{vnstat}) (type: file-like)
+The vnstat package.
+
+@item @code{database-dir} (default: @code{"/var/lib/vnstat"}) (type: string)
+Specifies the directory where the database is to be stored.  A full path
+must be given and a leading '/' isn't required.
+
+@item @code{#@{5-minute-hours@}#} (default: @code{48}) (type: maybe-integer)
+Data retention duration for the 5 minute resolution entries.  The
+configuration defines for how many past hours entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@item @code{#@{64bit-interface-counters@}#} (default: @code{-2}) (type: maybe-integer)
+Select interface counter handling.  Set to @code{1} for defining that
+all interfaces use 64-bit counters on the kernel side and @code{0} for
+defining 32-bit counter.  Set to @code{-1} for using the old style logic
+used in earlier versions where counter values within 32-bits are assumed
+to be 32-bit and anything larger is assumed to be a 64-bit counter.  This
+may produce false results if a 64-bit counter is reset within the
+32-bits.  Set to @code{-2} for using automatic detection based on
+available kernel datastructures.
+
+@item @code{always-add-new-interfaces?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable automatic creation of new database entries for
+interfaces not currently in the database even if the database file
+already exists when the daemon is started.  New database entries will
+also get created for new interfaces seen while the daemon is running.
+Pseudo interfaces lo, lo0 and sit0 are always excluded from getting
+added.
+
+@item @code{bandwidth-detection?} (default: @code{#t}) (type: maybe-boolean)
+Try to automatically detect @var{max-bandwidth} value for each monitored
+interface.  Mostly only ethernet interfaces support this feature.
+@var{max-bandwidth} will be used as fallback value if detection fails.
+Any interface specific @var{max-BW} configuration will disable the
+detection for the specified interface.  In Linux, the detection is
+disabled for tun interfaces due to the Linux kernel always reporting 10
+Mbit regardless of the used real interface.
+
+@item @code{bandwidth-detection-interval} (default: @code{5}) (type: maybe-integer)
+How often in minutes interface specific detection of @var{max-bandwidth}
+is done for detecting possible changes when @var{bandwidth-detection} is
+enabled.  Can be disabled by setting to @code{0}.  Value range:
+@samp{0}..@samp{30}
+
+@item @code{boot-variation} (default: @code{15}) (type: maybe-integer)
+Time in seconds how much the boot time reported by system kernel can
+variate between updates.  Value range: @samp{0}..@samp{300}
+
+@item @code{check-disk-space?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the availability check of at least some free disk
+space before a database write.
+
+@item @code{create-dirs?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the creation of directories when a configured path
+doesn't exist.  This includes @var{database-dir}.
+
+@item @code{daemon-group} (type: maybe-string)
+Specify the group to which the daemon process should switch during
+startup.  The group can either be the name of the group or a numerical
+group id.  Leave empty to disable group switching.  This option can only
+be used when the process is started as root.
+
+@item @code{daemon-user} (type: maybe-string)
+Specify the user to which the daemon process should switch during
+startup.  The user can either be the login of the user or a numerical
+user id.  Leave empty to disable user switching.  This option can only
+be used when the process is started as root.
+
+@item @code{daily-days} (default: @code{62}) (type: maybe-integer)
+Data retention duration for the one day resolution entries.  The
+configuration defines for how many past days entries will be stored.  Set
+to @code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{database-synchronous} (default: @code{-1}) (type: maybe-integer)
+Change the setting of the SQLite "synchronous" flag which controls how
+much care is taken to ensure disk writes have fully completed when
+writing data to the database before continuing other actions.  Higher
+values take extra steps to ensure data safety at the cost of slower
+performance.  A value of @code{0} will result in all handling being left
+to the filesystem itself.  Set to @code{-1} to select the default value
+according to database mode controlled by
+@var{database-write-ahead-logging} setting.  See SQLite documentation
+for more details regarding values from @code{1} to @code{3}.  Value
+range: @samp{-1}..@samp{3}
+
+@item @code{database-write-ahead-logging?} (default: @code{#f}) (type: maybe-boolean)
+Enable or disable SQLite Write-Ahead Logging mode for the database.  See
+SQLite documentation for more details and note that support for
+read-only operations isn't available in older SQLite versions.
+
+@item @code{hourly-days} (default: @code{4}) (type: maybe-integer)
+Data retention duration for the one hour resolution entries.  The
+configuration defines for how many past days entries will be stored.  Set
+to @code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{log-file} (type: maybe-string)
+Specify log file path and name to be used if @var{use-logging} is set to
+@code{1}.
+
+@item @code{max-bandwidth} (type: maybe-integer)
+Maximum bandwidth for all interfaces.  If the interface specific traffic
+exceeds the given value then the data is assumed to be invalid and
+rejected.  Set to 0 in order to disable the feature.  Value range:
+@samp{0}..@samp{50000}
+
+@item @code{max-BW} (type: maybe-alist)
+Same as @var{max-bandwidth} but can be used for setting individual
+limits for selected interfaces.  This is an association list of
+interfaces as symbols/strings to integer values.  For example,
+@lisp
+(max-BW `((eth0 .  15000)
+          (ppp0 .  10000)))
+@end lisp
+@var{bandwidth-detection} is disabled on an interface specific level for
+each @var{max-BW} configuration.  Value range: @samp{0}..@samp{50000}
+
+@item @code{monthly-months} (default: @code{25}) (type: maybe-integer)
+Data retention duration for the one month resolution entries.  The
+configuration defines for how many past months entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@item @code{month-rotate} (default: @code{1}) (type: maybe-integer)
+Day of month that months are expected to change.  Usually set to 1 but
+can be set to alternative values for example for tracking monthly billed
+traffic where the billing period doesn't start on the first day.  For
+example, if set to 7, days of February up to and including the 6th will
+count for January.  Changing this option will not cause existing data to
+be recalculated.  Value range: @samp{1}..@samp{28}
+
+@item @code{month-rotate-affects-years?} (default: @code{#f}) (type: maybe-boolean)
+Enable or disable @var{month-rotate} also affecting yearly data.
+Applicable only when @var{month-rotate} has a value greater than one.
+
+@item @code{offline-save-interval} (default: @code{30}) (type: maybe-integer)
+How often in minutes cached interface data is saved to file when all
+monitored interfaces are offline.  Value range:
+@var{save-interval}..@samp{60}
+
+@item @code{pid-file} (default: @code{"/var/run/vnstatd.pid"}) (type: maybe-string)
+Specify pid file path and name to be used.
+
+@item @code{poll-interval} (default: @code{5}) (type: maybe-integer)
+How often in seconds interfaces are checked for status changes.  Value
+range: @samp{2}..@samp{60}
+
+@item @code{rescan-database-on-save?} (type: maybe-boolean)
+Automatically discover added interfaces from the database and start
+monitoring.  The rescan is done every @var{save-interval} or
+@var{offline-save-interval} minutes depending on the current activity
+state.
+
+@item @code{save-interval} (default: @code{5}) (type: maybe-integer)
+How often in minutes cached interface data is saved to file.  Value
+range: ( @var{update-interval} / 60 )..@samp{60}
+
+@item @code{save-on-status-change?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the additional saving to file of cached interface data
+when the availability of an interface changes, i.e., when an interface
+goes offline or comes online.
+
+@item @code{time-sync-wait} (default: @code{5}) (type: maybe-integer)
+How many minutes to wait during daemon startup for system clock to sync
+if most recent database update appears to be in the future.  This may be
+needed in systems without a real-time clock (RTC) which require some
+time after boot to query and set the correct time.  @code{0} = wait
+disabled.  Value range: @samp{0}..@samp{60}
+
+@item @code{top-day-entries} (default: @code{20}) (type: maybe-integer)
+Data retention duration for the top day entries.  The configuration
+defines how many of the past top day entries will be stored.  Set to
+@code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{trafficless-entries?} (default: @code{#t}) (type: maybe-boolean)
+Create database entries even when there is no traffic during the entry's
+time period.
+
+@item @code{update-file-owner?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the update of file ownership during daemon process
+startup.  During daemon startup, only database, log and pid files will
+be modified if the user or group change feature ( @var{daemon-user} or
+@var{daemon-group} ) is enabled and the files don't match the requested
+user or group.  During manual database creation, this option will cause
+file ownership to be inherited from the database directory if the
+directory already exists.  This option only has effect when the process
+is started as root or via sudo.
+
+@item @code{update-interval} (default: @code{20}) (type: maybe-integer)
+How often in seconds the interface data is updated.  Value range:
+@var{poll-interval}..@samp{300}
+
+@item @code{use-logging} (default: @code{2}) (type: maybe-integer)
+Enable or disable logging.  This option is ignored when the daemon is
+started with .B "-n, --nodaemon" which results in all log output being
+shown in terminal the daemon process is using.  @code{0} = disabled,
+@code{1} = logfile and @code{2} = syslog.
+
+@item @code{use-UTC?} (type: maybe-boolean)
+Enable or disable using UTC as timezone in the database for all entries.
+When enabled, all entries added to the database will use UTC regardless
+of the configured system timezone.  When disabled, the configured system
+timezone will be used.  Changing this setting will not result in already
+existing data to be modified.
+
+@item @code{yearly-years} (default: @code{-1}) (type: maybe-integer)
+Data retention duration for the one year resolution entries.  The
+configuration defines for how many past years entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@end table
+@end deftp
+@c %end of fragment
+
 @subsubheading Zabbix server
 @cindex zabbix zabbix-server
 Zabbix is a high performance monitoring system that can collect data from a
diff --git a/gnu/services/monitoring.scm b/gnu/services/monitoring.scm
index 44e2e8886c..a83dcf315b 100644
--- a/gnu/services/monitoring.scm
+++ b/gnu/services/monitoring.scm
@@ -3,6 +3,7 @@
 ;;; Copyright © 2018, 2019 Gábor Boskovits <boskovits@gmail.com>
 ;;; Copyright © 2018, 2019, 2020 Oleg Pykhalov <go.wigust@gmail.com>
 ;;; Copyright © 2022 Marius Bakke <marius@gnu.org>
+;;; Copyright © 2023 Bruno Victal <mirai@makinata.eu>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -26,6 +27,7 @@ (define-module (gnu services monitoring)
   #:use-module (gnu services web)
   #:use-module (gnu packages admin)
   #:use-module (gnu packages monitoring)
+  #:use-module (gnu packages networking)
   #:use-module (gnu system shadow)
   #:use-module (guix gexp)
   #:use-module (guix packages)
@@ -34,6 +36,7 @@ (define-module (gnu services monitoring)
   #:use-module ((guix ui) #:select (display-hint G_))
   #:use-module (ice-9 match)
   #:use-module (ice-9 rdelim)
+  #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-26)
   #:use-module (srfi srfi-35)
   #:export (darkstat-configuration
@@ -45,6 +48,10 @@ (define-module (gnu services monitoring)
             prometheus-node-exporter-web-listen-address
             prometheus-node-exporter-service-type
 
+            vnstat-configuration
+            vnstat-configuration?
+            vnstat-service-type
+
             zabbix-server-configuration
             zabbix-server-service-type
             zabbix-agent-configuration
@@ -196,6 +203,412 @@ (define prometheus-node-exporter-service-type
                         prometheus-node-exporter-shepherd-service)))
    (default-value (prometheus-node-exporter-configuration))))
 
+\f
+;;;
+;;; vnstat daemon
+;;;
+
+(define* (camelfy-field-name field-name #:key (dromedary? #f))
+  (match (string-split (symbol->string field-name) #\-)
+    ((head tail ...)
+     (string-join (cons (if dromedary? head (string-upcase head 0 1))
+                        (map string-capitalize tail)) ""))))
+
+(define (dock-field-name field-name)
+  "Drop rightmost '?' character"
+  (let ((str (symbol->string field-name)))
+    (if (string-suffix? "?" str)
+        (string->symbol (string-drop-right str 1))
+        field-name)))
+
+(define (vnstat-serialize-string field-name value)
+  #~(format #f "~a ~s~%"
+            #$(camelfy-field-name field-name)
+            #$value))
+
+(define vnstat-serialize-integer vnstat-serialize-string)
+
+(define (vnstat-serialize-boolean field-name value)
+  #~(format #f "~a ~a~%"
+            #$(camelfy-field-name (dock-field-name field-name))
+            #$(if value 1 0)))
+
+(define (vnstat-serialize-alist field-name value)
+  (generic-serialize-alist string-append
+                           (lambda (iface val)
+                             (vnstat-serialize-integer
+                              (format #f "MaxBW~a" iface) val))
+                           value))
+
+(define-maybe string  (prefix vnstat-))
+(define-maybe integer (prefix vnstat-))
+(define-maybe boolean (prefix vnstat-))
+(define-maybe alist   (prefix vnstat-))
+
+;; Documentation strings from vnstat.conf manpage adapted to texinfo.
+;; vnstat checkout: v2.10, commit b3408af1c609aa6265d296cab7bfe59a61d7cf70
+(define-configuration vnstat-configuration
+  (package
+    (file-like vnstat)
+    "The vnstat package."
+    empty-serializer)
+
+  (database-dir
+   (string "/var/lib/vnstat")
+   "\
+Specifies the directory where the database is to be stored.
+A full path must be given and a leading '/' isn't required.")  
+
+  (5-minute-hours
+   (maybe-integer 48)
+   "\
+Data retention duration for the 5 minute resolution entries. The configuration
+defines for how many past hours entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (64bit-interface-counters
+   (maybe-integer -2)
+   "\
+Select interface counter handling. Set to @code{1} for defining that all interfaces
+use 64-bit counters on the kernel side and @code{0} for defining 32-bit counter. Set
+to @code{-1} for using the old style logic used in earlier versions where counter
+values within 32-bits are assumed to be 32-bit and anything larger is assumed to
+be a 64-bit counter. This may produce false results if a 64-bit counter is
+reset within the 32-bits. Set to @code{-2} for using automatic detection based on
+available kernel datastructures.")
+
+  (always-add-new-interfaces?
+   (maybe-boolean #t)
+   "\
+Enable or disable automatic creation of new database entries for interfaces not
+currently in the database even if the database file already exists when the
+daemon is started. New database entries will also get created for new interfaces
+seen while the daemon is running. Pseudo interfaces lo, lo0 and sit0 are always
+excluded from getting added.")
+
+  (bandwidth-detection?
+   (maybe-boolean #t)
+   "\
+Try to automatically detect
+@var{max-bandwidth}
+value for each monitored interface. Mostly only ethernet interfaces support
+this feature.
+@var{max-bandwidth}
+will be used as fallback value if detection fails. Any interface specific
+@var{max-BW}
+configuration will disable the detection for the specified interface.
+In Linux, the detection is disabled for tun interfaces due to the
+Linux kernel always reporting 10 Mbit regardless of the used real interface.")
+
+  (bandwidth-detection-interval
+   (maybe-integer 5)
+   "\
+How often in minutes interface specific detection of
+@var{max-bandwidth}
+is done for detecting possible changes when
+@var{bandwidth-detection}
+is enabled. Can be disabled by setting to @code{0}. Value range: @samp{0}..@samp{30}")
+
+  (boot-variation
+   (maybe-integer 15)
+   "\
+Time in seconds how much the boot time reported by system kernel can variate
+between updates. Value range: @samp{0}..@samp{300}")
+
+  (check-disk-space?
+   (maybe-boolean #t)
+   "\
+Enable or disable the availability check of at least some free disk space before
+a database write.")
+
+  (create-dirs?
+   (maybe-boolean #t)
+   "\
+Enable or disable the creation of directories when a configured path doesn't
+exist. This includes @var{database-dir}.")
+
+  (daemon-group
+   maybe-string
+   "\
+Specify the group to which the daemon process should switch during startup.
+The group can either be the name of the group or a numerical group id.
+Leave empty to disable group switching. This option can only be used when
+the process is started as root.")
+
+  (daemon-user
+   maybe-string
+   "\
+Specify the user to which the daemon process should switch during startup.
+The user can either be the login of the user or a numerical user id.
+Leave empty to disable user switching. This option can only be used when
+the process is started as root.")
+
+  (daily-days
+   (maybe-integer 62)
+   "\
+Data retention duration for the one day resolution entries. The configuration
+defines for how many past days entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (database-synchronous
+   (maybe-integer -1)
+   "\
+Change the setting of the SQLite \"synchronous\" flag which controls how much
+care is taken to ensure disk writes have fully completed when writing data to
+the database before continuing other actions. Higher values take extra steps
+to ensure data safety at the cost of slower performance. A value of @code{0} will
+result in all handling being left to the filesystem itself. Set to @code{-1} to
+select the default value according to database mode controlled by
+@var{database-write-ahead-logging}
+setting. See SQLite documentation for more details regarding values from @code{1}
+to @code{3}. Value range: @samp{-1}..@samp{3}")
+
+  (database-write-ahead-logging?
+   (maybe-boolean #f)
+   "\
+Enable or disable SQLite Write-Ahead Logging mode for the database. See SQLite
+documentation for more details and note that support for read-only operations
+isn't available in older SQLite versions.")
+
+  (hourly-days
+   (maybe-integer 4)
+   "\
+Data retention duration for the one hour resolution entries. The configuration
+defines for how many past days entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (log-file
+   maybe-string
+   "\
+Specify log file path and name to be used if @var{use-logging} is set to @code{1}.")
+
+  (max-bandwidth
+   maybe-integer
+   "\
+Maximum bandwidth for all interfaces. If the interface specific traffic
+exceeds the given value then the data is assumed to be invalid and rejected.
+Set to 0 in order to disable the feature. Value range: @samp{0}..@samp{50000}")
+
+  ;; documentation adapted for alist type
+  (max-BW
+   maybe-alist
+   "\
+Same as
+@var{max-bandwidth}
+but can be used for setting individual limits
+for selected interfaces. This is an association list of interfaces
+as symbols/strings to integer values. For example,
+@lisp
+(max-BW
+ `((eth0 . 15000)
+   (ppp0 . 10000)))
+@end lisp
+@var{bandwidth-detection}
+is disabled on an interface specific level for each
+@var{max-BW}
+configuration. Value range: @samp{0}..@samp{50000}")
+
+  (monthly-months
+   (maybe-integer 25)
+   "\
+Data retention duration for the one month resolution entries. The configuration
+defines for how many past months entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (month-rotate
+   (maybe-integer 1)
+   "\
+Day of month that months are expected to change. Usually set to
+1 but can be set to alternative values for example for tracking
+monthly billed traffic where the billing period doesn't start on
+the first day. For example, if set to 7, days of February up to and
+including the 6th will count for January. Changing this option will
+not cause existing data to be recalculated. Value range: @samp{1}..@samp{28}")
+
+  (month-rotate-affects-years?
+   (maybe-boolean #f)
+   "\
+Enable or disable
+@var{month-rotate}
+also affecting yearly data. Applicable only when
+@var{month-rotate}
+has a value greater than one.")
+
+  (offline-save-interval
+   (maybe-integer 30)
+   "\
+How often in minutes cached interface data is saved to file when all monitored
+interfaces are offline. Value range:
+@var{save-interval}..@samp{60}")
+
+  (pid-file
+   (maybe-string "/var/run/vnstatd.pid")
+   "\
+Specify pid file path and name to be used.")
+
+  (poll-interval
+   (maybe-integer 5)
+   "\
+How often in seconds interfaces are checked for status changes.
+Value range: @samp{2}..@samp{60}")
+
+  (rescan-database-on-save?
+   maybe-boolean
+   "\
+Automatically discover added interfaces from the database and start monitoring.
+The rescan is done every
+@var{save-interval}
+or
+@var{offline-save-interval}
+minutes depending on the current activity state.")
+
+  (save-interval
+   (maybe-integer 5)
+   "\
+How often in minutes cached interface data is saved to file.
+Value range: (
+@var{update-interval} / 60 )..@samp{60}")
+
+  (save-on-status-change?
+   (maybe-boolean #t)
+   "\
+Enable or disable the additional saving to file of cached interface data
+when the availability of an interface changes, i.e., when an interface goes
+offline or comes online.")
+
+  (time-sync-wait
+   (maybe-integer 5)
+   "\
+How many minutes to wait during daemon startup for system clock to sync if
+most recent database update appears to be in the future. This may be needed
+in systems without a real-time clock (RTC) which require some time after boot
+to query and set the correct time. @code{0} = wait disabled.
+Value range: @samp{0}..@samp{60}")
+
+  (top-day-entries
+   (maybe-integer 20)
+   "\
+Data retention duration for the top day entries. The configuration
+defines how many of the past top day entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (trafficless-entries?
+   (maybe-boolean #t)
+   "\
+Create database entries even when there is no traffic during the entry's time
+period.")
+
+  (update-file-owner?
+   (maybe-boolean #t)
+   "\
+Enable or disable the update of file ownership during daemon process startup.
+During daemon startup, only database, log and pid files will be modified if the
+user or group change feature (
+@var{daemon-user}
+or
+@var{daemon-group}
+) is enabled and the files don't match the requested user or group. During manual
+database creation, this option will cause file ownership to be inherited from the
+database directory if the directory already exists. This option only has effect
+when the process is started as root or via sudo.")
+
+  (update-interval
+   (maybe-integer 20)
+   "\
+How often in seconds the interface data is updated. Value range:
+@var{poll-interval}..@samp{300}")
+
+  (use-logging
+   (maybe-integer 2)
+   "\
+Enable or disable logging. This option is ignored when the daemon is started with
+.B \"-n, --nodaemon\"
+which results in all log output being shown in terminal the daemon process is using.
+@code{0} = disabled, @code{1} = logfile and @code{2} = syslog.")
+
+  (use-UTC?
+   maybe-boolean
+   "\
+Enable or disable using UTC as timezone in the database for all entries. When
+enabled, all entries added to the database will use UTC regardless of the
+configured system timezone. When disabled, the configured system timezone
+will be used. Changing this setting will not result in already existing
+data to be modified.")
+
+  (yearly-years
+   (maybe-integer -1)
+   "\
+Data retention duration for the one year resolution entries. The configuration
+defines for how many past years entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (prefix vnstat-))
+
+(define (vnstat-serialize-configuration config)
+  (mixed-text-file
+   "vnstat.conf"
+   (serialize-configuration config vnstat-configuration-fields)))
+
+(define (vnstat-shepherd-service config)
+  (let ((config-file (vnstat-serialize-configuration config)))
+    (match-record config <vnstat-configuration> (package pid-file)
+      (shepherd-service
+       (documentation "Run vnstatd.")
+       (requirement `(networking))
+       (provision '(vnstatd))
+       (start #~(make-forkexec-constructor
+                 (list #$(file-append package "/sbin/vnstatd")
+                       "--daemon"
+                       "--config" #$config-file)
+                 #:pid-file #$pid-file))
+       (stop #~(make-kill-destructor))
+       (actions
+        (list (shepherd-configuration-action config-file)
+              (shepherd-action
+               (name 'reload)
+               (documentation "Reload vnstatd.")
+               (procedure
+                #~(lambda (pid)
+                    (if pid
+                        (begin
+                          (kill pid SIGHUP)
+                          (format #t
+                                  "Issued SIGHUP to vnstatd (PID ~a)."
+                                  pid))
+                        (format #t "vnstatd is not running.")))))))))))
+
+(define (vnstat-account-service config)
+  (match-record config <vnstat-configuration> (daemon-group daemon-user)
+    (if (every maybe-value-set? (list daemon-group daemon-user))
+        (list
+         (user-group
+          (name daemon-group)
+          (system? #t))
+         (user-account
+          (name daemon-user)
+          (group daemon-group)
+          (system? #t)
+          (home-directory "/var/empty")
+          (shell (file-append shadow "/sbin/nologin"))))
+        '())))
+
+(define vnstat-service-type
+  (service-type
+   (name 'vnstat)
+   (description "vnStat network-traffic monitor service.")
+   (extensions
+    (list (service-extension shepherd-root-service-type
+                             (compose list vnstat-shepherd-service))
+          (service-extension account-service-type
+                             vnstat-account-service)))
+   (default-value (vnstat-configuration))))
+
 \f
 ;;;
 ;;; Zabbix server

base-commit: 0f85081ed1d99be57d3544e0307e7fa9ca043be9
-- 
2.38.1





^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH v2] services: Add vnstat-service-type.
  2023-01-13 20:07 [bug#60788] [PATCH] services: Add vnstat-service-type Bruno Victal
@ 2023-01-14 21:06 ` Bruno Victal
  2023-01-16 18:42 ` [bug#60788] [PATCH] " Maxim Cournoyer
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 27+ messages in thread
From: Bruno Victal @ 2023-01-14 21:06 UTC (permalink / raw)
  To: 60788; +Cc: Bruno Victal

* gnu/services/monitoring.scm (vnstat-service-type): New variable.
* doc/guix.texi (Monitoring Services): Document it.
---

Fix for camelfy-field-name.

 doc/guix.texi               | 238 +++++++++++++++++++++
 gnu/services/monitoring.scm | 413 ++++++++++++++++++++++++++++++++++++
 2 files changed, 651 insertions(+)

diff --git a/doc/guix.texi b/doc/guix.texi
index c07ec89b2f..1f6e5d9ed6 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -111,6 +111,7 @@
 Copyright @copyright{} 2022 John Kehayias@*
 Copyright @copyright{} 2022 Ivan Vilata-i-Balaguer@*
 Copyright @copyright{} 2023 Giacomo Leidi@*
+Copyright @copyright{} 2023 Bruno Victal@*
 
 Permission is granted to copy, distribute and/or modify this document
 under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -28219,6 +28220,243 @@ Monitoring Services
 @end table
 @end deftp
 
+@anchor{vnstat}
+@subsubheading vnStat Network Traffic Monitor
+@cindex vnstat
+
+vnStat is a network traffic monitor that uses interface statistics provided
+by the kernel rather than traffic sniffing.  This makes it a light resource
+monitor, regardless of network traffic rate.
+
+@defvar vnstat-service-type
+This is the service type for the @uref{https://humdi.net/vnstat/,vnStat} daemon
+and accepts a @code{vnstat-configuration} value.
+@end defvar
+
+@c %start of fragment
+@deftp {Data Type} vnstat-configuration
+Available @code{vnstat-configuration} fields are:
+
+@table @asis
+@item @code{package} (default: @code{vnstat}) (type: file-like)
+The vnstat package.
+
+@item @code{database-dir} (default: @code{"/var/lib/vnstat"}) (type: string)
+Specifies the directory where the database is to be stored.  A full path
+must be given and a leading '/' isn't required.
+
+@item @code{#@{5-minute-hours@}#} (default: @code{48}) (type: maybe-integer)
+Data retention duration for the 5 minute resolution entries.  The
+configuration defines for how many past hours entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@item @code{#@{64bit-interface-counters@}#} (default: @code{-2}) (type: maybe-integer)
+Select interface counter handling.  Set to @code{1} for defining that
+all interfaces use 64-bit counters on the kernel side and @code{0} for
+defining 32-bit counter.  Set to @code{-1} for using the old style logic
+used in earlier versions where counter values within 32-bits are assumed
+to be 32-bit and anything larger is assumed to be a 64-bit counter.  This
+may produce false results if a 64-bit counter is reset within the
+32-bits.  Set to @code{-2} for using automatic detection based on
+available kernel datastructures.
+
+@item @code{always-add-new-interfaces?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable automatic creation of new database entries for
+interfaces not currently in the database even if the database file
+already exists when the daemon is started.  New database entries will
+also get created for new interfaces seen while the daemon is running.
+Pseudo interfaces lo, lo0 and sit0 are always excluded from getting
+added.
+
+@item @code{bandwidth-detection?} (default: @code{#t}) (type: maybe-boolean)
+Try to automatically detect @var{max-bandwidth} value for each monitored
+interface.  Mostly only ethernet interfaces support this feature.
+@var{max-bandwidth} will be used as fallback value if detection fails.
+Any interface specific @var{max-BW} configuration will disable the
+detection for the specified interface.  In Linux, the detection is
+disabled for tun interfaces due to the Linux kernel always reporting 10
+Mbit regardless of the used real interface.
+
+@item @code{bandwidth-detection-interval} (default: @code{5}) (type: maybe-integer)
+How often in minutes interface specific detection of @var{max-bandwidth}
+is done for detecting possible changes when @var{bandwidth-detection} is
+enabled.  Can be disabled by setting to @code{0}.  Value range:
+@samp{0}..@samp{30}
+
+@item @code{boot-variation} (default: @code{15}) (type: maybe-integer)
+Time in seconds how much the boot time reported by system kernel can
+variate between updates.  Value range: @samp{0}..@samp{300}
+
+@item @code{check-disk-space?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the availability check of at least some free disk
+space before a database write.
+
+@item @code{create-dirs?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the creation of directories when a configured path
+doesn't exist.  This includes @var{database-dir}.
+
+@item @code{daemon-group} (type: maybe-string)
+Specify the group to which the daemon process should switch during
+startup.  The group can either be the name of the group or a numerical
+group id.  Leave empty to disable group switching.  This option can only
+be used when the process is started as root.
+
+@item @code{daemon-user} (type: maybe-string)
+Specify the user to which the daemon process should switch during
+startup.  The user can either be the login of the user or a numerical
+user id.  Leave empty to disable user switching.  This option can only
+be used when the process is started as root.
+
+@item @code{daily-days} (default: @code{62}) (type: maybe-integer)
+Data retention duration for the one day resolution entries.  The
+configuration defines for how many past days entries will be stored.  Set
+to @code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{database-synchronous} (default: @code{-1}) (type: maybe-integer)
+Change the setting of the SQLite "synchronous" flag which controls how
+much care is taken to ensure disk writes have fully completed when
+writing data to the database before continuing other actions.  Higher
+values take extra steps to ensure data safety at the cost of slower
+performance.  A value of @code{0} will result in all handling being left
+to the filesystem itself.  Set to @code{-1} to select the default value
+according to database mode controlled by
+@var{database-write-ahead-logging} setting.  See SQLite documentation
+for more details regarding values from @code{1} to @code{3}.  Value
+range: @samp{-1}..@samp{3}
+
+@item @code{database-write-ahead-logging?} (default: @code{#f}) (type: maybe-boolean)
+Enable or disable SQLite Write-Ahead Logging mode for the database.  See
+SQLite documentation for more details and note that support for
+read-only operations isn't available in older SQLite versions.
+
+@item @code{hourly-days} (default: @code{4}) (type: maybe-integer)
+Data retention duration for the one hour resolution entries.  The
+configuration defines for how many past days entries will be stored.  Set
+to @code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{log-file} (type: maybe-string)
+Specify log file path and name to be used if @var{use-logging} is set to
+@code{1}.
+
+@item @code{max-bandwidth} (type: maybe-integer)
+Maximum bandwidth for all interfaces.  If the interface specific traffic
+exceeds the given value then the data is assumed to be invalid and
+rejected.  Set to 0 in order to disable the feature.  Value range:
+@samp{0}..@samp{50000}
+
+@item @code{max-BW} (type: maybe-alist)
+Same as @var{max-bandwidth} but can be used for setting individual
+limits for selected interfaces.  This is an association list of
+interfaces as symbols/strings to integer values.  For example,
+@lisp
+(max-BW `((eth0 .  15000)
+          (ppp0 .  10000)))
+@end lisp
+@var{bandwidth-detection} is disabled on an interface specific level for
+each @var{max-BW} configuration.  Value range: @samp{0}..@samp{50000}
+
+@item @code{monthly-months} (default: @code{25}) (type: maybe-integer)
+Data retention duration for the one month resolution entries.  The
+configuration defines for how many past months entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@item @code{month-rotate} (default: @code{1}) (type: maybe-integer)
+Day of month that months are expected to change.  Usually set to 1 but
+can be set to alternative values for example for tracking monthly billed
+traffic where the billing period doesn't start on the first day.  For
+example, if set to 7, days of February up to and including the 6th will
+count for January.  Changing this option will not cause existing data to
+be recalculated.  Value range: @samp{1}..@samp{28}
+
+@item @code{month-rotate-affects-years?} (default: @code{#f}) (type: maybe-boolean)
+Enable or disable @var{month-rotate} also affecting yearly data.
+Applicable only when @var{month-rotate} has a value greater than one.
+
+@item @code{offline-save-interval} (default: @code{30}) (type: maybe-integer)
+How often in minutes cached interface data is saved to file when all
+monitored interfaces are offline.  Value range:
+@var{save-interval}..@samp{60}
+
+@item @code{pid-file} (default: @code{"/var/run/vnstatd.pid"}) (type: maybe-string)
+Specify pid file path and name to be used.
+
+@item @code{poll-interval} (default: @code{5}) (type: maybe-integer)
+How often in seconds interfaces are checked for status changes.  Value
+range: @samp{2}..@samp{60}
+
+@item @code{rescan-database-on-save?} (type: maybe-boolean)
+Automatically discover added interfaces from the database and start
+monitoring.  The rescan is done every @var{save-interval} or
+@var{offline-save-interval} minutes depending on the current activity
+state.
+
+@item @code{save-interval} (default: @code{5}) (type: maybe-integer)
+How often in minutes cached interface data is saved to file.  Value
+range: ( @var{update-interval} / 60 )..@samp{60}
+
+@item @code{save-on-status-change?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the additional saving to file of cached interface data
+when the availability of an interface changes, i.e., when an interface
+goes offline or comes online.
+
+@item @code{time-sync-wait} (default: @code{5}) (type: maybe-integer)
+How many minutes to wait during daemon startup for system clock to sync
+if most recent database update appears to be in the future.  This may be
+needed in systems without a real-time clock (RTC) which require some
+time after boot to query and set the correct time.  @code{0} = wait
+disabled.  Value range: @samp{0}..@samp{60}
+
+@item @code{top-day-entries} (default: @code{20}) (type: maybe-integer)
+Data retention duration for the top day entries.  The configuration
+defines how many of the past top day entries will be stored.  Set to
+@code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{trafficless-entries?} (default: @code{#t}) (type: maybe-boolean)
+Create database entries even when there is no traffic during the entry's
+time period.
+
+@item @code{update-file-owner?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the update of file ownership during daemon process
+startup.  During daemon startup, only database, log and pid files will
+be modified if the user or group change feature ( @var{daemon-user} or
+@var{daemon-group} ) is enabled and the files don't match the requested
+user or group.  During manual database creation, this option will cause
+file ownership to be inherited from the database directory if the
+directory already exists.  This option only has effect when the process
+is started as root or via sudo.
+
+@item @code{update-interval} (default: @code{20}) (type: maybe-integer)
+How often in seconds the interface data is updated.  Value range:
+@var{poll-interval}..@samp{300}
+
+@item @code{use-logging} (default: @code{2}) (type: maybe-integer)
+Enable or disable logging.  This option is ignored when the daemon is
+started with .B "-n, --nodaemon" which results in all log output being
+shown in terminal the daemon process is using.  @code{0} = disabled,
+@code{1} = logfile and @code{2} = syslog.
+
+@item @code{use-UTC?} (type: maybe-boolean)
+Enable or disable using UTC as timezone in the database for all entries.
+When enabled, all entries added to the database will use UTC regardless
+of the configured system timezone.  When disabled, the configured system
+timezone will be used.  Changing this setting will not result in already
+existing data to be modified.
+
+@item @code{yearly-years} (default: @code{-1}) (type: maybe-integer)
+Data retention duration for the one year resolution entries.  The
+configuration defines for how many past years entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@end table
+@end deftp
+@c %end of fragment
+
 @subsubheading Zabbix server
 @cindex zabbix zabbix-server
 Zabbix is a high performance monitoring system that can collect data from a
diff --git a/gnu/services/monitoring.scm b/gnu/services/monitoring.scm
index 44e2e8886c..7017c677ea 100644
--- a/gnu/services/monitoring.scm
+++ b/gnu/services/monitoring.scm
@@ -3,6 +3,7 @@
 ;;; Copyright © 2018, 2019 Gábor Boskovits <boskovits@gmail.com>
 ;;; Copyright © 2018, 2019, 2020 Oleg Pykhalov <go.wigust@gmail.com>
 ;;; Copyright © 2022 Marius Bakke <marius@gnu.org>
+;;; Copyright © 2023 Bruno Victal <mirai@makinata.eu>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -26,6 +27,7 @@ (define-module (gnu services monitoring)
   #:use-module (gnu services web)
   #:use-module (gnu packages admin)
   #:use-module (gnu packages monitoring)
+  #:use-module (gnu packages networking)
   #:use-module (gnu system shadow)
   #:use-module (guix gexp)
   #:use-module (guix packages)
@@ -34,6 +36,7 @@ (define-module (gnu services monitoring)
   #:use-module ((guix ui) #:select (display-hint G_))
   #:use-module (ice-9 match)
   #:use-module (ice-9 rdelim)
+  #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-26)
   #:use-module (srfi srfi-35)
   #:export (darkstat-configuration
@@ -45,6 +48,10 @@ (define-module (gnu services monitoring)
             prometheus-node-exporter-web-listen-address
             prometheus-node-exporter-service-type
 
+            vnstat-configuration
+            vnstat-configuration?
+            vnstat-service-type
+
             zabbix-server-configuration
             zabbix-server-service-type
             zabbix-agent-configuration
@@ -196,6 +203,412 @@ (define prometheus-node-exporter-service-type
                         prometheus-node-exporter-shepherd-service)))
    (default-value (prometheus-node-exporter-configuration))))
 
+\f
+;;;
+;;; vnstat daemon
+;;;
+
+(define* (camelfy-field-name field-name #:key (dromedary? #f))
+  (match (string-split (symbol->string field-name) #\-)
+    ((head tail ...)
+     (string-join (cons (if dromedary? head (string-upcase head 0 1))
+                        (map (cut string-upcase <> 0 1) tail)) ""))))
+
+(define (dock-field-name field-name)
+  "Drop rightmost '?' character"
+  (let ((str (symbol->string field-name)))
+    (if (string-suffix? "?" str)
+        (string->symbol (string-drop-right str 1))
+        field-name)))
+
+(define (vnstat-serialize-string field-name value)
+  #~(format #f "~a ~s~%"
+            #$(camelfy-field-name field-name)
+            #$value))
+
+(define vnstat-serialize-integer vnstat-serialize-string)
+
+(define (vnstat-serialize-boolean field-name value)
+  #~(format #f "~a ~a~%"
+            #$(camelfy-field-name (dock-field-name field-name))
+            #$(if value 1 0)))
+
+(define (vnstat-serialize-alist field-name value)
+  (generic-serialize-alist string-append
+                           (lambda (iface val)
+                             (vnstat-serialize-integer
+                              (format #f "MaxBW~a" iface) val))
+                           value))
+
+(define-maybe string  (prefix vnstat-))
+(define-maybe integer (prefix vnstat-))
+(define-maybe boolean (prefix vnstat-))
+(define-maybe alist   (prefix vnstat-))
+
+;; Documentation strings from vnstat.conf manpage adapted to texinfo.
+;; vnstat checkout: v2.10, commit b3408af1c609aa6265d296cab7bfe59a61d7cf70
+(define-configuration vnstat-configuration
+  (package
+    (file-like vnstat)
+    "The vnstat package."
+    empty-serializer)
+
+  (database-dir
+   (string "/var/lib/vnstat")
+   "\
+Specifies the directory where the database is to be stored.
+A full path must be given and a leading '/' isn't required.")  
+
+  (5-minute-hours
+   (maybe-integer 48)
+   "\
+Data retention duration for the 5 minute resolution entries. The configuration
+defines for how many past hours entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (64bit-interface-counters
+   (maybe-integer -2)
+   "\
+Select interface counter handling. Set to @code{1} for defining that all interfaces
+use 64-bit counters on the kernel side and @code{0} for defining 32-bit counter. Set
+to @code{-1} for using the old style logic used in earlier versions where counter
+values within 32-bits are assumed to be 32-bit and anything larger is assumed to
+be a 64-bit counter. This may produce false results if a 64-bit counter is
+reset within the 32-bits. Set to @code{-2} for using automatic detection based on
+available kernel datastructures.")
+
+  (always-add-new-interfaces?
+   (maybe-boolean #t)
+   "\
+Enable or disable automatic creation of new database entries for interfaces not
+currently in the database even if the database file already exists when the
+daemon is started. New database entries will also get created for new interfaces
+seen while the daemon is running. Pseudo interfaces lo, lo0 and sit0 are always
+excluded from getting added.")
+
+  (bandwidth-detection?
+   (maybe-boolean #t)
+   "\
+Try to automatically detect
+@var{max-bandwidth}
+value for each monitored interface. Mostly only ethernet interfaces support
+this feature.
+@var{max-bandwidth}
+will be used as fallback value if detection fails. Any interface specific
+@var{max-BW}
+configuration will disable the detection for the specified interface.
+In Linux, the detection is disabled for tun interfaces due to the
+Linux kernel always reporting 10 Mbit regardless of the used real interface.")
+
+  (bandwidth-detection-interval
+   (maybe-integer 5)
+   "\
+How often in minutes interface specific detection of
+@var{max-bandwidth}
+is done for detecting possible changes when
+@var{bandwidth-detection}
+is enabled. Can be disabled by setting to @code{0}. Value range: @samp{0}..@samp{30}")
+
+  (boot-variation
+   (maybe-integer 15)
+   "\
+Time in seconds how much the boot time reported by system kernel can variate
+between updates. Value range: @samp{0}..@samp{300}")
+
+  (check-disk-space?
+   (maybe-boolean #t)
+   "\
+Enable or disable the availability check of at least some free disk space before
+a database write.")
+
+  (create-dirs?
+   (maybe-boolean #t)
+   "\
+Enable or disable the creation of directories when a configured path doesn't
+exist. This includes @var{database-dir}.")
+
+  (daemon-group
+   maybe-string
+   "\
+Specify the group to which the daemon process should switch during startup.
+The group can either be the name of the group or a numerical group id.
+Leave empty to disable group switching. This option can only be used when
+the process is started as root.")
+
+  (daemon-user
+   maybe-string
+   "\
+Specify the user to which the daemon process should switch during startup.
+The user can either be the login of the user or a numerical user id.
+Leave empty to disable user switching. This option can only be used when
+the process is started as root.")
+
+  (daily-days
+   (maybe-integer 62)
+   "\
+Data retention duration for the one day resolution entries. The configuration
+defines for how many past days entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (database-synchronous
+   (maybe-integer -1)
+   "\
+Change the setting of the SQLite \"synchronous\" flag which controls how much
+care is taken to ensure disk writes have fully completed when writing data to
+the database before continuing other actions. Higher values take extra steps
+to ensure data safety at the cost of slower performance. A value of @code{0} will
+result in all handling being left to the filesystem itself. Set to @code{-1} to
+select the default value according to database mode controlled by
+@var{database-write-ahead-logging}
+setting. See SQLite documentation for more details regarding values from @code{1}
+to @code{3}. Value range: @samp{-1}..@samp{3}")
+
+  (database-write-ahead-logging?
+   (maybe-boolean #f)
+   "\
+Enable or disable SQLite Write-Ahead Logging mode for the database. See SQLite
+documentation for more details and note that support for read-only operations
+isn't available in older SQLite versions.")
+
+  (hourly-days
+   (maybe-integer 4)
+   "\
+Data retention duration for the one hour resolution entries. The configuration
+defines for how many past days entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (log-file
+   maybe-string
+   "\
+Specify log file path and name to be used if @var{use-logging} is set to @code{1}.")
+
+  (max-bandwidth
+   maybe-integer
+   "\
+Maximum bandwidth for all interfaces. If the interface specific traffic
+exceeds the given value then the data is assumed to be invalid and rejected.
+Set to 0 in order to disable the feature. Value range: @samp{0}..@samp{50000}")
+
+  ;; documentation adapted for alist type
+  (max-BW
+   maybe-alist
+   "\
+Same as
+@var{max-bandwidth}
+but can be used for setting individual limits
+for selected interfaces. This is an association list of interfaces
+as symbols/strings to integer values. For example,
+@lisp
+(max-BW
+ `((eth0 . 15000)
+   (ppp0 . 10000)))
+@end lisp
+@var{bandwidth-detection}
+is disabled on an interface specific level for each
+@var{max-BW}
+configuration. Value range: @samp{0}..@samp{50000}")
+
+  (monthly-months
+   (maybe-integer 25)
+   "\
+Data retention duration for the one month resolution entries. The configuration
+defines for how many past months entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (month-rotate
+   (maybe-integer 1)
+   "\
+Day of month that months are expected to change. Usually set to
+1 but can be set to alternative values for example for tracking
+monthly billed traffic where the billing period doesn't start on
+the first day. For example, if set to 7, days of February up to and
+including the 6th will count for January. Changing this option will
+not cause existing data to be recalculated. Value range: @samp{1}..@samp{28}")
+
+  (month-rotate-affects-years?
+   (maybe-boolean #f)
+   "\
+Enable or disable
+@var{month-rotate}
+also affecting yearly data. Applicable only when
+@var{month-rotate}
+has a value greater than one.")
+
+  (offline-save-interval
+   (maybe-integer 30)
+   "\
+How often in minutes cached interface data is saved to file when all monitored
+interfaces are offline. Value range:
+@var{save-interval}..@samp{60}")
+
+  (pid-file
+   (maybe-string "/var/run/vnstatd.pid")
+   "\
+Specify pid file path and name to be used.")
+
+  (poll-interval
+   (maybe-integer 5)
+   "\
+How often in seconds interfaces are checked for status changes.
+Value range: @samp{2}..@samp{60}")
+
+  (rescan-database-on-save?
+   maybe-boolean
+   "\
+Automatically discover added interfaces from the database and start monitoring.
+The rescan is done every
+@var{save-interval}
+or
+@var{offline-save-interval}
+minutes depending on the current activity state.")
+
+  (save-interval
+   (maybe-integer 5)
+   "\
+How often in minutes cached interface data is saved to file.
+Value range: (
+@var{update-interval} / 60 )..@samp{60}")
+
+  (save-on-status-change?
+   (maybe-boolean #t)
+   "\
+Enable or disable the additional saving to file of cached interface data
+when the availability of an interface changes, i.e., when an interface goes
+offline or comes online.")
+
+  (time-sync-wait
+   (maybe-integer 5)
+   "\
+How many minutes to wait during daemon startup for system clock to sync if
+most recent database update appears to be in the future. This may be needed
+in systems without a real-time clock (RTC) which require some time after boot
+to query and set the correct time. @code{0} = wait disabled.
+Value range: @samp{0}..@samp{60}")
+
+  (top-day-entries
+   (maybe-integer 20)
+   "\
+Data retention duration for the top day entries. The configuration
+defines how many of the past top day entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (trafficless-entries?
+   (maybe-boolean #t)
+   "\
+Create database entries even when there is no traffic during the entry's time
+period.")
+
+  (update-file-owner?
+   (maybe-boolean #t)
+   "\
+Enable or disable the update of file ownership during daemon process startup.
+During daemon startup, only database, log and pid files will be modified if the
+user or group change feature (
+@var{daemon-user}
+or
+@var{daemon-group}
+) is enabled and the files don't match the requested user or group. During manual
+database creation, this option will cause file ownership to be inherited from the
+database directory if the directory already exists. This option only has effect
+when the process is started as root or via sudo.")
+
+  (update-interval
+   (maybe-integer 20)
+   "\
+How often in seconds the interface data is updated. Value range:
+@var{poll-interval}..@samp{300}")
+
+  (use-logging
+   (maybe-integer 2)
+   "\
+Enable or disable logging. This option is ignored when the daemon is started with
+.B \"-n, --nodaemon\"
+which results in all log output being shown in terminal the daemon process is using.
+@code{0} = disabled, @code{1} = logfile and @code{2} = syslog.")
+
+  (use-UTC?
+   maybe-boolean
+   "\
+Enable or disable using UTC as timezone in the database for all entries. When
+enabled, all entries added to the database will use UTC regardless of the
+configured system timezone. When disabled, the configured system timezone
+will be used. Changing this setting will not result in already existing
+data to be modified.")
+
+  (yearly-years
+   (maybe-integer -1)
+   "\
+Data retention duration for the one year resolution entries. The configuration
+defines for how many past years entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (prefix vnstat-))
+
+(define (vnstat-serialize-configuration config)
+  (mixed-text-file
+   "vnstat.conf"
+   (serialize-configuration config vnstat-configuration-fields)))
+
+(define (vnstat-shepherd-service config)
+  (let ((config-file (vnstat-serialize-configuration config)))
+    (match-record config <vnstat-configuration> (package pid-file)
+      (shepherd-service
+       (documentation "Run vnstatd.")
+       (requirement `(networking))
+       (provision '(vnstatd))
+       (start #~(make-forkexec-constructor
+                 (list #$(file-append package "/sbin/vnstatd")
+                       "--daemon"
+                       "--config" #$config-file)
+                 #:pid-file #$pid-file))
+       (stop #~(make-kill-destructor))
+       (actions
+        (list (shepherd-configuration-action config-file)
+              (shepherd-action
+               (name 'reload)
+               (documentation "Reload vnstatd.")
+               (procedure
+                #~(lambda (pid)
+                    (if pid
+                        (begin
+                          (kill pid SIGHUP)
+                          (format #t
+                                  "Issued SIGHUP to vnstatd (PID ~a)."
+                                  pid))
+                        (format #t "vnstatd is not running.")))))))))))
+
+(define (vnstat-account-service config)
+  (match-record config <vnstat-configuration> (daemon-group daemon-user)
+    (if (every maybe-value-set? (list daemon-group daemon-user))
+        (list
+         (user-group
+          (name daemon-group)
+          (system? #t))
+         (user-account
+          (name daemon-user)
+          (group daemon-group)
+          (system? #t)
+          (home-directory "/var/empty")
+          (shell (file-append shadow "/sbin/nologin"))))
+        '())))
+
+(define vnstat-service-type
+  (service-type
+   (name 'vnstat)
+   (description "vnStat network-traffic monitor service.")
+   (extensions
+    (list (service-extension shepherd-root-service-type
+                             (compose list vnstat-shepherd-service))
+          (service-extension account-service-type
+                             vnstat-account-service)))
+   (default-value (vnstat-configuration))))
+
 \f
 ;;;
 ;;; Zabbix server

base-commit: fb3085e80fd3d4559057b60d9e2c1c1f1ff51022
-- 
2.38.1





^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH] services: Add vnstat-service-type.
  2023-01-13 20:07 [bug#60788] [PATCH] services: Add vnstat-service-type Bruno Victal
  2023-01-14 21:06 ` [bug#60788] [PATCH v2] " Bruno Victal
@ 2023-01-16 18:42 ` Maxim Cournoyer
  2023-01-16 19:31   ` Bruno Victal
  2023-01-18  0:34 ` [bug#60788] [PATCH v2] " Bruno Victal
                   ` (11 subsequent siblings)
  13 siblings, 1 reply; 27+ messages in thread
From: Maxim Cournoyer @ 2023-01-16 18:42 UTC (permalink / raw)
  To: Bruno Victal; +Cc: 60788

Hello!

Bruno Victal <mirai@makinata.eu> writes:

[...]

> @@ -45,6 +48,10 @@ (define-module (gnu services monitoring)
>              prometheus-node-exporter-web-listen-address
>              prometheus-node-exporter-service-type
 
> +            vnstat-configuration
> +            vnstat-configuration?
> +            vnstat-service-type
> +

Normally, all the accessors are exported, otherwise there's no means to
introspect a vnstat-configuration, which may be useful at the REPL.

[...]

> +\f
> +;;;
> +;;; vnstat daemon
> +;;;
> +
> +(define* (camelfy-field-name field-name #:key (dromedary? #f))
> +  (match (string-split (symbol->string field-name) #\-)
> +    ((head tail ...)
> +     (string-join (cons (if dromedary? head (string-upcase head 0 1))
> +                        (map string-capitalize tail)) ""))))

I'd name this pascal-case-field-name, and drop the apparently unused
dromerady? argument (fun, though :-)).  If we need it, we could
introduce a 2nd camel-case-field-name.

> +(define (dock-field-name field-name)
> +  "Drop rightmost '?' character"
> +  (let ((str (symbol->string field-name)))
> +    (if (string-suffix? "?" str)
> +        (string->symbol (string-drop-right str 1))
> +        field-name)))

I'm not sure about the meaning of 'dock' in this procedure name.
Perhaps 'strip-trailing-?-character' would be better?

> +(define (vnstat-serialize-string field-name value)
> +  #~(format #f "~a ~s~%"
> +            #$(camelfy-field-name field-name)
> +            #$value))
> +
> +(define vnstat-serialize-integer vnstat-serialize-string)
> +
> +(define (vnstat-serialize-boolean field-name value)
> +  #~(format #f "~a ~a~%"
> +            #$(camelfy-field-name (dock-field-name field-name))
> +            #$(if value 1 0)))
> +
> +(define (vnstat-serialize-alist field-name value)
> +  (generic-serialize-alist string-append
> +                           (lambda (iface val)
> +                             (vnstat-serialize-integer
> +                              (format #f "MaxBW~a" iface) val))
> +                           value))
> +
> +(define-maybe string  (prefix vnstat-))
> +(define-maybe integer (prefix vnstat-))
> +(define-maybe boolean (prefix vnstat-))
> +(define-maybe alist   (prefix vnstat-))
> +
> +;; Documentation strings from vnstat.conf manpage adapted to texinfo.
> +;; vnstat checkout: v2.10, commit b3408af1c609aa6265d296cab7bfe59a61d7cf70
> +(define-configuration vnstat-configuration
> +  (package
> +    (file-like vnstat)
> +    "The vnstat package."
> +    empty-serializer)
> +
> +  (database-dir
> +   (string "/var/lib/vnstat")
> +   "\
> +Specifies the directory where the database is to be stored.
> +A full path must be given and a leading '/' isn't required.")  
> +
> +  (5-minute-hours
> +   (maybe-integer 48)
> +   "\
> +Data retention duration for the 5 minute resolution entries. The configuration
> +defines for how many past hours entries will be stored. Set to @code{-1} for
> +unlimited entries or to @code{0} to disable the data collection of this
> +resolution.")
> +
> +  (64bit-interface-counters
> +   (maybe-integer -2)
> +   "\
> +Select interface counter handling. Set to @code{1} for defining that all interfaces
> +use 64-bit counters on the kernel side and @code{0} for defining 32-bit counter. Set
> +to @code{-1} for using the old style logic used in earlier versions where counter
> +values within 32-bits are assumed to be 32-bit and anything larger is assumed to
> +be a 64-bit counter. This may produce false results if a 64-bit counter is
> +reset within the 32-bits. Set to @code{-2} for using automatic detection based on
> +available kernel datastructures.")

Please reflow these paragraphs so they fit under the 80 characters width
limit.  Perhaps drop the \ escape, as it doesn't provide much and looks
a bit worst, in my opinion.  Each sentence should be separated by two
spaces (that's a Texinfo convention).  This comment applies to all
similar documentation texts.

> +  (always-add-new-interfaces?
> +   (maybe-boolean #t)
> +   "\
> +Enable or disable automatic creation of new database entries for interfaces not
> +currently in the database even if the database file already exists when the
> +daemon is started. New database entries will also get created for new interfaces
> +seen while the daemon is running. Pseudo interfaces lo, lo0 and sit0 are always
> +excluded from getting added.")

The lo, lo0 and sit0 could use a @samp{} or decorator.

[...]

> +(define (vnstat-serialize-configuration config)
> +  (mixed-text-file
> +   "vnstat.conf"
> +   (serialize-configuration config vnstat-configuration-fields)))
> +
> +(define (vnstat-shepherd-service config)
> +  (let ((config-file (vnstat-serialize-configuration config)))
> +    (match-record config <vnstat-configuration> (package pid-file)
> +      (shepherd-service
> +       (documentation "Run vnstatd.")
> +       (requirement `(networking))
> +       (provision '(vnstatd))
> +       (start #~(make-forkexec-constructor
> +                 (list #$(file-append package "/sbin/vnstatd")
> +                       "--daemon"
> +                       "--config" #$config-file)
> +                 #:pid-file #$pid-file))
> +       (stop #~(make-kill-destructor))
> +       (actions
> +        (list (shepherd-configuration-action config-file)

I don't understand what (shepherd-configuration-action config-file)
corresponds to?

> +              (shepherd-action
> +               (name 'reload)
> +               (documentation "Reload vnstatd.")
> +               (procedure
> +                #~(lambda (pid)
> +                    (if pid
> +                        (begin
> +                          (kill pid SIGHUP)
> +                          (format #t
> +                                  "Issued SIGHUP to vnstatd (PID ~a)."
> +                                  pid))
> +                        (format #t "vnstatd is not running.")))))))))))

> +(define (vnstat-account-service config)
> +  (match-record config <vnstat-configuration> (daemon-group daemon-user)
> +    (if (every maybe-value-set? (list daemon-group daemon-user))
> +        (list
> +         (user-group
> +          (name daemon-group)
> +          (system? #t))
> +         (user-account
> +          (name daemon-user)
> +          (group daemon-group)
> +          (system? #t)
> +          (home-directory "/var/empty")
> +          (shell (file-append shadow "/sbin/nologin"))))
> +        '())))
> +
> +(define vnstat-service-type
> +  (service-type
> +   (name 'vnstat)
> +   (description "vnStat network-traffic monitor service.")
> +   (extensions
> +    (list (service-extension shepherd-root-service-type
> +                             (compose list vnstat-shepherd-service))
> +          (service-extension account-service-type
> +                             vnstat-account-service)))
> +   (default-value (vnstat-configuration))))

The rest LGTM (a system test would be nice, but since the Shepherd
service definition is straightforward, it could come later, when the
need arise).

That appears carefully crafted, thank you!  Could you send a v2 with my
above comments addressed, keeping me in CC?

-- 
Thanks,
Maxim




^ permalink raw reply	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH] services: Add vnstat-service-type.
  2023-01-16 18:42 ` [bug#60788] [PATCH] " Maxim Cournoyer
@ 2023-01-16 19:31   ` Bruno Victal
  2023-01-16 19:56     ` Maxim Cournoyer
  0 siblings, 1 reply; 27+ messages in thread
From: Bruno Victal @ 2023-01-16 19:31 UTC (permalink / raw)
  To: Maxim Cournoyer; +Cc: Maxim Cournoyer, 60788

On 2023-01-16 18:42, Maxim Cournoyer wrote:

>> Bruno Victal <mirai@makinata.eu> writes:
>> +;;;
>> +;;; vnstat daemon
>> +;;;
>> +
>> +(define* (camelfy-field-name field-name #:key (dromedary? #f))
>> +  (match (string-split (symbol->string field-name) #\-)
>> +    ((head tail ...)
>> +     (string-join (cons (if dromedary? head (string-upcase head 0 1))
>> +                        (map string-capitalize tail)) ""))))
> 
> I'd name this pascal-case-field-name, and drop the apparently unused
> dromerady? argument (fun, though :-)).  If we need it, we could
> introduce a 2nd camel-case-field-name.

I was anticipating that this snippet would get reused (i.e. copy-pasted) by
other services so I wanted to generalize it before it starts spawning
its dromedary cousin.

If this isn't preferred, I can drop it and just leave it as
pascal-case-field-name. (maybe these kinds of procedures could go into
a new "configuration utils" module with useful auxiliary functions?)

> 
>> +(define (dock-field-name field-name)
>> +  "Drop rightmost '?' character"
>> +  (let ((str (symbol->string field-name)))
>> +    (if (string-suffix? "?" str)
>> +        (string->symbol (string-drop-right str 1))
>> +        field-name)))
> 
> I'm not sure about the meaning of 'dock' in this procedure name.
> Perhaps 'strip-trailing-?-character' would be better?

I couldn't think of any other word that would essentially describe
whats amounts to "dropping" the tail of a list/string.
(here, 'dock': "the practice of cutting off or trimming the tail of an animal")

>> +;; Documentation strings from vnstat.conf manpage adapted to texinfo.
>> +;; vnstat checkout: v2.10, commit b3408af1c609aa6265d296cab7bfe59a61d7cf70
>> +(define-configuration vnstat-configuration
>> +  (package
>> +    (file-like vnstat)
>> +    "The vnstat package."
>> +    empty-serializer)
>> +
>> +  (database-dir
>> +   (string "/var/lib/vnstat")
>> +   "\
>> +Specifies the directory where the database is to be stored.
>> +A full path must be given and a leading '/' isn't required.")  
>> +
>> +  (5-minute-hours
>> +   (maybe-integer 48)
>> +   "\
>> +Data retention duration for the 5 minute resolution entries. The configuration
>> +defines for how many past hours entries will be stored. Set to @code{-1} for
>> +unlimited entries or to @code{0} to disable the data collection of this
>> +resolution.")
>> +
>> +  (64bit-interface-counters
>> +   (maybe-integer -2)
>> +   "\
>> +Select interface counter handling. Set to @code{1} for defining that all interfaces
>> +use 64-bit counters on the kernel side and @code{0} for defining 32-bit counter. Set
>> +to @code{-1} for using the old style logic used in earlier versions where counter
>> +values within 32-bits are assumed to be 32-bit and anything larger is assumed to
>> +be a 64-bit counter. This may produce false results if a 64-bit counter is
>> +reset within the 32-bits. Set to @code{-2} for using automatic detection based on
>> +available kernel datastructures.")
> 
> Please reflow these paragraphs so they fit under the 80 characters width
> limit.  Perhaps drop the \ escape, as it doesn't provide much and looks
> a bit worst, in my opinion.  Each sentence should be separated by two
> spaces (that's a Texinfo convention).  This comment applies to all
> similar documentation texts.

They're formatted this way as I find it easier to compare against upstream troff sources.
If they're reflowed or if the \ escape is dropped then the diffs get larger to compare against
(most of the changes here are easier to compare using word-diffs)

>> +  (always-add-new-interfaces?
>> +   (maybe-boolean #t)
>> +   "\
>> +Enable or disable automatic creation of new database entries for interfaces not
>> +currently in the database even if the database file already exists when the
>> +daemon is started. New database entries will also get created for new interfaces
>> +seen while the daemon is running. Pseudo interfaces lo, lo0 and sit0 are always
>> +excluded from getting added.")
> 
> The lo, lo0 and sit0 could use a @samp{} or decorator.

Noted.

> 
> [...]
> 
>> +(define (vnstat-serialize-configuration config)
>> +  (mixed-text-file
>> +   "vnstat.conf"
>> +   (serialize-configuration config vnstat-configuration-fields)))
>> +
>> +(define (vnstat-shepherd-service config)
>> +  (let ((config-file (vnstat-serialize-configuration config)))
>> +    (match-record config <vnstat-configuration> (package pid-file)
>> +      (shepherd-service
>> +       (documentation "Run vnstatd.")
>> +       (requirement `(networking))
>> +       (provision '(vnstatd))
>> +       (start #~(make-forkexec-constructor
>> +                 (list #$(file-append package "/sbin/vnstatd")
>> +                       "--daemon"
>> +                       "--config" #$config-file)
>> +                 #:pid-file #$pid-file))
>> +       (stop #~(make-kill-destructor))
>> +       (actions
>> +        (list (shepherd-configuration-action config-file)
> 
> I don't understand what (shepherd-configuration-action config-file)
> corresponds to?

It shows the path to the config file with `herd configuration vnstatd'.
It was introduced with 'ebc7de6a1efb702fd0364128cbde19697966c4f4'.

>> +              (shepherd-action
>> +               (name 'reload)
>> +               (documentation "Reload vnstatd.")
>> +               (procedure
>> +                #~(lambda (pid)
>> +                    (if pid
>> +                        (begin
>> +                          (kill pid SIGHUP)
>> +                          (format #t
>> +                                  "Issued SIGHUP to vnstatd (PID ~a)."
>> +                                  pid))
>> +                        (format #t "vnstatd is not running.")))))))))))
> 
>> +(define (vnstat-account-service config)
>> +  (match-record config <vnstat-configuration> (daemon-group daemon-user)
>> +    (if (every maybe-value-set? (list daemon-group daemon-user))
>> +        (list
>> +         (user-group
>> +          (name daemon-group)
>> +          (system? #t))
>> +         (user-account
>> +          (name daemon-user)
>> +          (group daemon-group)
>> +          (system? #t)
>> +          (home-directory "/var/empty")
>> +          (shell (file-append shadow "/sbin/nologin"))))
>> +        '())))
>> +
>> +(define vnstat-service-type
>> +  (service-type
>> +   (name 'vnstat)
>> +   (description "vnStat network-traffic monitor service.")
>> +   (extensions
>> +    (list (service-extension shepherd-root-service-type
>> +                             (compose list vnstat-shepherd-service))
>> +          (service-extension account-service-type
>> +                             vnstat-account-service)))
>> +   (default-value (vnstat-configuration))))
> 
> The rest LGTM (a system test would be nice, but since the Shepherd
> service definition is straightforward, it could come later, when the
> need arise).

I've thought a bit about the system test though the way vnstat works is that
it needs to collect some data and it also takes a while for the daemon to flag it
as ready (~5 minutes). This would be a somewhat slow and non-trivial test to write.


WDYT?



Cheers,
Bruno




^ permalink raw reply	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH] services: Add vnstat-service-type.
  2023-01-16 19:31   ` Bruno Victal
@ 2023-01-16 19:56     ` Maxim Cournoyer
  0 siblings, 0 replies; 27+ messages in thread
From: Maxim Cournoyer @ 2023-01-16 19:56 UTC (permalink / raw)
  To: Bruno Victal; +Cc: 60788

Hi Bruno,

Bruno Victal <mirai@makinata.eu> writes:

> On 2023-01-16 18:42, Maxim Cournoyer wrote:
>
>>> Bruno Victal <mirai@makinata.eu> writes:
>>> +;;;
>>> +;;; vnstat daemon
>>> +;;;
>>> +
>>> +(define* (camelfy-field-name field-name #:key (dromedary? #f))
>>> +  (match (string-split (symbol->string field-name) #\-)
>>> +    ((head tail ...)
>>> +     (string-join (cons (if dromedary? head (string-upcase head 0 1))
>>> +                        (map string-capitalize tail)) ""))))
>> 
>> I'd name this pascal-case-field-name, and drop the apparently unused
>> dromerady? argument (fun, though :-)).  If we need it, we could
>> introduce a 2nd camel-case-field-name.
>
> I was anticipating that this snippet would get reused (i.e. copy-pasted) by
> other services so I wanted to generalize it before it starts spawning
> its dromedary cousin.
>
> If this isn't preferred, I can drop it and just leave it as
> pascal-case-field-name. (maybe these kinds of procedures could go into
> a new "configuration utils" module with useful auxiliary functions?)
>
>> 
>>> +(define (dock-field-name field-name)
>>> +  "Drop rightmost '?' character"
>>> +  (let ((str (symbol->string field-name)))
>>> +    (if (string-suffix? "?" str)
>>> +        (string->symbol (string-drop-right str 1))
>>> +        field-name)))
>> 
>> I'm not sure about the meaning of 'dock' in this procedure name.
>> Perhaps 'strip-trailing-?-character' would be better?
>
> I couldn't think of any other word that would essentially describe
> whats amounts to "dropping" the tail of a list/string.
> (here, 'dock': "the practice of cutting off or trimming the tail of an animal")

Ouch.  I'm even more in favor of an alternative name now that I know the
meaning of 'dock' :-).

>>> +;; Documentation strings from vnstat.conf manpage adapted to texinfo.
>>> +;; vnstat checkout: v2.10, commit b3408af1c609aa6265d296cab7bfe59a61d7cf70
>>> +(define-configuration vnstat-configuration
>>> +  (package
>>> +    (file-like vnstat)
>>> +    "The vnstat package."
>>> +    empty-serializer)
>>> +
>>> +  (database-dir
>>> +   (string "/var/lib/vnstat")
>>> +   "\
>>> +Specifies the directory where the database is to be stored.
>>> +A full path must be given and a leading '/' isn't required.")  
>>> +
>>> +  (5-minute-hours
>>> +   (maybe-integer 48)
>>> +   "\
>>> +Data retention duration for the 5 minute resolution entries. The configuration
>>> +defines for how many past hours entries will be stored. Set to @code{-1} for
>>> +unlimited entries or to @code{0} to disable the data collection of this
>>> +resolution.")
>>> +
>>> +  (64bit-interface-counters
>>> +   (maybe-integer -2)
>>> +   "\
>>> +Select interface counter handling. Set to @code{1} for defining that all interfaces
>>> +use 64-bit counters on the kernel side and @code{0} for defining 32-bit counter. Set
>>> +to @code{-1} for using the old style logic used in earlier versions where counter
>>> +values within 32-bits are assumed to be 32-bit and anything larger is assumed to
>>> +be a 64-bit counter. This may produce false results if a 64-bit counter is
>>> +reset within the 32-bits. Set to @code{-2} for using automatic detection based on
>>> +available kernel datastructures.")
>> 
>> Please reflow these paragraphs so they fit under the 80 characters width
>> limit.  Perhaps drop the \ escape, as it doesn't provide much and looks
>> a bit worst, in my opinion.  Each sentence should be separated by two
>> spaces (that's a Texinfo convention).  This comment applies to all
>> similar documentation texts.
>
> They're formatted this way as I find it easier to compare against upstream troff sources.
> If they're reflowed or if the \ escape is dropped then the diffs get larger to compare against
> (most of the changes here are easier to compare using word-diffs)

I think we should take ownership of that text, and update it when it
makes sense without necessarily having to keep every details the same
with upstream (especially for the double space rule and other
conventions); in this view I'd opt to adjust it so the Guix source reads
beautifully rather than for diffing against the orginal source :-).

If this truly provides value to you (to be able to more easily diff
against the troff source), then perhaps how to do so should be detailed
in a comment for the next person to understand how this works and why it
is this way.

>>> +  (always-add-new-interfaces?
>>> +   (maybe-boolean #t)
>>> +   "\
>>> +Enable or disable automatic creation of new database entries for interfaces not
>>> +currently in the database even if the database file already exists when the
>>> +daemon is started. New database entries will also get created for new interfaces
>>> +seen while the daemon is running. Pseudo interfaces lo, lo0 and sit0 are always
>>> +excluded from getting added.")
>> 
>> The lo, lo0 and sit0 could use a @samp{} or decorator.
>
> Noted.
>
>> 
>> [...]
>> 
>>> +(define (vnstat-serialize-configuration config)
>>> +  (mixed-text-file
>>> +   "vnstat.conf"
>>> +   (serialize-configuration config vnstat-configuration-fields)))
>>> +
>>> +(define (vnstat-shepherd-service config)
>>> +  (let ((config-file (vnstat-serialize-configuration config)))
>>> +    (match-record config <vnstat-configuration> (package pid-file)
>>> +      (shepherd-service
>>> +       (documentation "Run vnstatd.")
>>> +       (requirement `(networking))
>>> +       (provision '(vnstatd))
>>> +       (start #~(make-forkexec-constructor
>>> +                 (list #$(file-append package "/sbin/vnstatd")
>>> +                       "--daemon"
>>> +                       "--config" #$config-file)
>>> +                 #:pid-file #$pid-file))
>>> +       (stop #~(make-kill-destructor))
>>> +       (actions
>>> +        (list (shepherd-configuration-action config-file)
>> 
>> I don't understand what (shepherd-configuration-action config-file)
>> corresponds to?
>
> It shows the path to the config file with `herd configuration vnstatd'.
> It was introduced with 'ebc7de6a1efb702fd0364128cbde19697966c4f4'.

Thanks!  That's news to me :-).

>>> +              (shepherd-action
>>> +               (name 'reload)
>>> +               (documentation "Reload vnstatd.")
>>> +               (procedure
>>> +                #~(lambda (pid)
>>> +                    (if pid
>>> +                        (begin
>>> +                          (kill pid SIGHUP)
>>> +                          (format #t
>>> +                                  "Issued SIGHUP to vnstatd (PID ~a)."
>>> +                                  pid))
>>> +                        (format #t "vnstatd is not running.")))))))))))
>> 
>>> +(define (vnstat-account-service config)
>>> +  (match-record config <vnstat-configuration> (daemon-group daemon-user)
>>> +    (if (every maybe-value-set? (list daemon-group daemon-user))
>>> +        (list
>>> +         (user-group
>>> +          (name daemon-group)
>>> +          (system? #t))
>>> +         (user-account
>>> +          (name daemon-user)
>>> +          (group daemon-group)
>>> +          (system? #t)
>>> +          (home-directory "/var/empty")
>>> +          (shell (file-append shadow "/sbin/nologin"))))
>>> +        '())))
>>> +
>>> +(define vnstat-service-type
>>> +  (service-type
>>> +   (name 'vnstat)
>>> +   (description "vnStat network-traffic monitor service.")
>>> +   (extensions
>>> +    (list (service-extension shepherd-root-service-type
>>> +                             (compose list vnstat-shepherd-service))
>>> +          (service-extension account-service-type
>>> +                             vnstat-account-service)))
>>> +   (default-value (vnstat-configuration))))
>> 
>> The rest LGTM (a system test would be nice, but since the Shepherd
>> service definition is straightforward, it could come later, when the
>> need arise).
>
> I've thought a bit about the system test though the way vnstat works is that
> it needs to collect some data and it also takes a while for the daemon to flag it
> as ready (~5 minutes). This would be a somewhat slow and non-trivial test to write.

Hm, not nice indeed.  Then I guess it's fine without a test.

One last thing: in general I think we should try to have our services
run in a Linux namespace (unprivileged); have you tried running vnstat
in a wrapper produced with least-authority-wrapper (from (guix
least-authority)) ?

-- 
Thanks,
Maxim




^ permalink raw reply	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH v2] services: Add vnstat-service-type.
  2023-01-13 20:07 [bug#60788] [PATCH] services: Add vnstat-service-type Bruno Victal
  2023-01-14 21:06 ` [bug#60788] [PATCH v2] " Bruno Victal
  2023-01-16 18:42 ` [bug#60788] [PATCH] " Maxim Cournoyer
@ 2023-01-18  0:34 ` Bruno Victal
  2023-01-18  0:37 ` [bug#60788] [PATCH v2] services: vnstat: Use least-authority-wrapper Bruno Victal
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 27+ messages in thread
From: Bruno Victal @ 2023-01-18  0:34 UTC (permalink / raw)
  To: 60788; +Cc: Bruno Victal, maxim.cournoyer

* gnu/services/monitoring.scm (vnstat-service-type): New variable.
* doc/guix.texi (Monitoring Services): Document it.
---
 doc/guix.texi               | 238 +++++++++++++++++++++
 gnu/services/monitoring.scm | 415 ++++++++++++++++++++++++++++++++++++
 2 files changed, 653 insertions(+)

diff --git a/doc/guix.texi b/doc/guix.texi
index 9b478733eb..6bda366750 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -112,6 +112,7 @@
 Copyright @copyright{} 2022 Ivan Vilata-i-Balaguer@*
 Copyright @copyright{} 2023 Giacomo Leidi@*
 Copyright @copyright{} 2022 Antero Mejr@*
+Copyright @copyright{} 2023 Bruno Victal@*
 
 Permission is granted to copy, distribute and/or modify this document
 under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -28266,6 +28267,243 @@ Monitoring Services
 @end table
 @end deftp
 
+@anchor{vnstat}
+@subsubheading vnStat Network Traffic Monitor
+@cindex vnstat
+
+vnStat is a network traffic monitor that uses interface statistics provided
+by the kernel rather than traffic sniffing.  This makes it a light resource
+monitor, regardless of network traffic rate.
+
+@defvar vnstat-service-type
+This is the service type for the @uref{https://humdi.net/vnstat/,vnStat} daemon
+and accepts a @code{vnstat-configuration} value.
+@end defvar
+
+@c %start of fragment
+@deftp {Data Type} vnstat-configuration
+Available @code{vnstat-configuration} fields are:
+
+@table @asis
+@item @code{package} (default: @code{vnstat}) (type: file-like)
+The vnstat package.
+
+@item @code{database-dir} (default: @code{"/var/lib/vnstat"}) (type: string)
+Specifies the directory where the database is to be stored.  A full path
+must be given and a leading '/' isn't required.
+
+@item @code{5-minute-hours} (default: @code{48}) (type: maybe-integer)
+Data retention duration for the 5 minute resolution entries.  The
+configuration defines for how many past hours entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@item @code{64bit-interface-counters} (default: @code{-2}) (type: maybe-integer)
+Select interface counter handling.  Set to @code{1} for defining that
+all interfaces use 64-bit counters on the kernel side and @code{0} for
+defining 32-bit counter.  Set to @code{-1} for using the old style logic
+used in earlier versions where counter values within 32-bits are assumed
+to be 32-bit and anything larger is assumed to be a 64-bit counter.  This
+may produce false results if a 64-bit counter is reset within the
+32-bits.  Set to @code{-2} for using automatic detection based on
+available kernel datastructures.
+
+@item @code{always-add-new-interfaces?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable automatic creation of new database entries for
+interfaces not currently in the database even if the database file
+already exists when the daemon is started.  New database entries will
+also get created for new interfaces seen while the daemon is running.
+Pseudo interfaces @samp{lo}, @samp{lo0} and @samp{sit0} are always excluded from getting
+added.
+
+@item @code{bandwidth-detection?} (default: @code{#t}) (type: maybe-boolean)
+Try to automatically detect @var{max-bandwidth} value for each monitored
+interface.  Mostly only ethernet interfaces support this feature.
+@var{max-bandwidth} will be used as fallback value if detection fails.
+Any interface specific @var{max-BW} configuration will disable the
+detection for the specified interface.  In Linux, the detection is
+disabled for tun interfaces due to the Linux kernel always reporting 10
+Mbit regardless of the used real interface.
+
+@item @code{bandwidth-detection-interval} (default: @code{5}) (type: maybe-integer)
+How often in minutes interface specific detection of @var{max-bandwidth}
+is done for detecting possible changes when @var{bandwidth-detection} is
+enabled.  Can be disabled by setting to @code{0}.  Value range:
+@samp{0}..@samp{30}
+
+@item @code{boot-variation} (default: @code{15}) (type: maybe-integer)
+Time in seconds how much the boot time reported by system kernel can
+variate between updates.  Value range: @samp{0}..@samp{300}
+
+@item @code{check-disk-space?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the availability check of at least some free disk
+space before a database write.
+
+@item @code{create-dirs?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the creation of directories when a configured path
+doesn't exist.  This includes @var{database-dir}.
+
+@item @code{daemon-group} (type: maybe-string)
+Specify the group to which the daemon process should switch during
+startup.  The group can either be the name of the group or a numerical
+group id.  Leave empty to disable group switching.  This option can only
+be used when the process is started as root.
+
+@item @code{daemon-user} (type: maybe-string)
+Specify the user to which the daemon process should switch during
+startup.  The user can either be the login of the user or a numerical
+user id.  Leave empty to disable user switching.  This option can only
+be used when the process is started as root.
+
+@item @code{daily-days} (default: @code{62}) (type: maybe-integer)
+Data retention duration for the one day resolution entries.  The
+configuration defines for how many past days entries will be stored.  Set
+to @code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{database-synchronous} (default: @code{-1}) (type: maybe-integer)
+Change the setting of the SQLite "synchronous" flag which controls how
+much care is taken to ensure disk writes have fully completed when
+writing data to the database before continuing other actions.  Higher
+values take extra steps to ensure data safety at the cost of slower
+performance.  A value of @code{0} will result in all handling being left
+to the filesystem itself.  Set to @code{-1} to select the default value
+according to database mode controlled by
+@var{database-write-ahead-logging} setting.  See SQLite documentation
+for more details regarding values from @code{1} to @code{3}.  Value
+range: @samp{-1}..@samp{3}
+
+@item @code{database-write-ahead-logging?} (default: @code{#f}) (type: maybe-boolean)
+Enable or disable SQLite Write-Ahead Logging mode for the database.  See
+SQLite documentation for more details and note that support for
+read-only operations isn't available in older SQLite versions.
+
+@item @code{hourly-days} (default: @code{4}) (type: maybe-integer)
+Data retention duration for the one hour resolution entries.  The
+configuration defines for how many past days entries will be stored.  Set
+to @code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{log-file} (type: maybe-string)
+Specify log file path and name to be used if @var{use-logging} is set to
+@code{1}.
+
+@item @code{max-bandwidth} (type: maybe-integer)
+Maximum bandwidth for all interfaces.  If the interface specific traffic
+exceeds the given value then the data is assumed to be invalid and
+rejected.  Set to 0 in order to disable the feature.  Value range:
+@samp{0}..@samp{50000}
+
+@item @code{max-BW} (type: maybe-alist)
+Same as @var{max-bandwidth} but can be used for setting individual
+limits for selected interfaces.  This is an association list of
+interfaces as symbols/strings to integer values.  For example,
+@lisp
+(max-BW `((eth0 .  15000)
+          (ppp0 .  10000)))
+@end lisp
+@var{bandwidth-detection} is disabled on an interface specific level for
+each @var{max-BW} configuration.  Value range: @samp{0}..@samp{50000}
+
+@item @code{monthly-months} (default: @code{25}) (type: maybe-integer)
+Data retention duration for the one month resolution entries.  The
+configuration defines for how many past months entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@item @code{month-rotate} (default: @code{1}) (type: maybe-integer)
+Day of month that months are expected to change.  Usually set to 1 but
+can be set to alternative values for example for tracking monthly billed
+traffic where the billing period doesn't start on the first day.  For
+example, if set to 7, days of February up to and including the 6th will
+count for January.  Changing this option will not cause existing data to
+be recalculated.  Value range: @samp{1}..@samp{28}
+
+@item @code{month-rotate-affects-years?} (default: @code{#f}) (type: maybe-boolean)
+Enable or disable @var{month-rotate} also affecting yearly data.
+Applicable only when @var{month-rotate} has a value greater than one.
+
+@item @code{offline-save-interval} (default: @code{30}) (type: maybe-integer)
+How often in minutes cached interface data is saved to file when all
+monitored interfaces are offline.  Value range:
+@var{save-interval}..@samp{60}
+
+@item @code{pid-file} (default: @code{"/var/run/vnstatd.pid"}) (type: maybe-string)
+Specify pid file path and name to be used.
+
+@item @code{poll-interval} (default: @code{5}) (type: maybe-integer)
+How often in seconds interfaces are checked for status changes.  Value
+range: @samp{2}..@samp{60}
+
+@item @code{rescan-database-on-save?} (type: maybe-boolean)
+Automatically discover added interfaces from the database and start
+monitoring.  The rescan is done every @var{save-interval} or
+@var{offline-save-interval} minutes depending on the current activity
+state.
+
+@item @code{save-interval} (default: @code{5}) (type: maybe-integer)
+How often in minutes cached interface data is saved to file.  Value
+range: ( @var{update-interval} / 60 )..@samp{60}
+
+@item @code{save-on-status-change?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the additional saving to file of cached interface data
+when the availability of an interface changes, i.e., when an interface
+goes offline or comes online.
+
+@item @code{time-sync-wait} (default: @code{5}) (type: maybe-integer)
+How many minutes to wait during daemon startup for system clock to sync
+if most recent database update appears to be in the future.  This may be
+needed in systems without a real-time clock (RTC) which require some
+time after boot to query and set the correct time.  @code{0} = wait
+disabled.  Value range: @samp{0}..@samp{60}
+
+@item @code{top-day-entries} (default: @code{20}) (type: maybe-integer)
+Data retention duration for the top day entries.  The configuration
+defines how many of the past top day entries will be stored.  Set to
+@code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{trafficless-entries?} (default: @code{#t}) (type: maybe-boolean)
+Create database entries even when there is no traffic during the entry's
+time period.
+
+@item @code{update-file-owner?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the update of file ownership during daemon process
+startup.  During daemon startup, only database, log and pid files will
+be modified if the user or group change feature ( @var{daemon-user} or
+@var{daemon-group} ) is enabled and the files don't match the requested
+user or group.  During manual database creation, this option will cause
+file ownership to be inherited from the database directory if the
+directory already exists.  This option only has effect when the process
+is started as root or via sudo.
+
+@item @code{update-interval} (default: @code{20}) (type: maybe-integer)
+How often in seconds the interface data is updated.  Value range:
+@var{poll-interval}..@samp{300}
+
+@item @code{use-logging} (default: @code{2}) (type: maybe-integer)
+Enable or disable logging.  This option is ignored when the daemon is
+started with .B "-n, --nodaemon" which results in all log output being
+shown in terminal the daemon process is using.  @code{0} = disabled,
+@code{1} = logfile and @code{2} = syslog.
+
+@item @code{use-UTC?} (type: maybe-boolean)
+Enable or disable using UTC as timezone in the database for all entries.
+When enabled, all entries added to the database will use UTC regardless
+of the configured system timezone.  When disabled, the configured system
+timezone will be used.  Changing this setting will not result in already
+existing data to be modified.
+
+@item @code{yearly-years} (default: @code{-1}) (type: maybe-integer)
+Data retention duration for the one year resolution entries.  The
+configuration defines for how many past years entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@end table
+@end deftp
+@c %end of fragment
+
 @subsubheading Zabbix server
 @cindex zabbix zabbix-server
 Zabbix is a high performance monitoring system that can collect data from a
diff --git a/gnu/services/monitoring.scm b/gnu/services/monitoring.scm
index 44e2e8886c..78fc49da5c 100644
--- a/gnu/services/monitoring.scm
+++ b/gnu/services/monitoring.scm
@@ -3,6 +3,7 @@
 ;;; Copyright © 2018, 2019 Gábor Boskovits <boskovits@gmail.com>
 ;;; Copyright © 2018, 2019, 2020 Oleg Pykhalov <go.wigust@gmail.com>
 ;;; Copyright © 2022 Marius Bakke <marius@gnu.org>
+;;; Copyright © 2023 Bruno Victal <mirai@makinata.eu>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -26,6 +27,7 @@ (define-module (gnu services monitoring)
   #:use-module (gnu services web)
   #:use-module (gnu packages admin)
   #:use-module (gnu packages monitoring)
+  #:use-module (gnu packages networking)
   #:use-module (gnu system shadow)
   #:use-module (guix gexp)
   #:use-module (guix packages)
@@ -34,6 +36,7 @@ (define-module (gnu services monitoring)
   #:use-module ((guix ui) #:select (display-hint G_))
   #:use-module (ice-9 match)
   #:use-module (ice-9 rdelim)
+  #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-26)
   #:use-module (srfi srfi-35)
   #:export (darkstat-configuration
@@ -45,6 +48,10 @@ (define-module (gnu services monitoring)
             prometheus-node-exporter-web-listen-address
             prometheus-node-exporter-service-type
 
+            vnstat-configuration
+            vnstat-configuration?
+            vnstat-service-type
+
             zabbix-server-configuration
             zabbix-server-service-type
             zabbix-agent-configuration
@@ -196,6 +203,414 @@ (define prometheus-node-exporter-service-type
                         prometheus-node-exporter-shepherd-service)))
    (default-value (prometheus-node-exporter-configuration))))
 
+\f
+;;;
+;;; vnstat daemon
+;;;
+
+(define* (camelfy-field-name field-name #:key (dromedary? #f))
+  (match (string-split (symbol->string field-name) #\-)
+    ((head tail ...)
+     (string-join (cons (if dromedary? head (string-upcase head 0 1))
+                        (map (cut string-upcase <> 0 1) tail)) ""))))
+
+(define (strip-trailing-?-character field-name)
+  "Drop rightmost '?' character"
+  (let ((str (symbol->string field-name)))
+    (if (string-suffix? "?" str)
+        (string->symbol (string-drop-right str 1))
+        field-name)))
+
+(define (vnstat-serialize-string field-name value)
+  #~(format #f "~a ~s~%"
+            #$(camelfy-field-name field-name)
+            #$value))
+
+(define vnstat-serialize-integer vnstat-serialize-string)
+
+(define (vnstat-serialize-boolean field-name value)
+  #~(format #f "~a ~a~%"
+            #$(camelfy-field-name (strip-trailing-?-character field-name))
+            #$(if value 1 0)))
+
+(define (vnstat-serialize-alist field-name value)
+  (generic-serialize-alist string-append
+                           (lambda (iface val)
+                             (vnstat-serialize-integer
+                              (format #f "MaxBW~a" iface) val))
+                           value))
+
+(define-maybe string  (prefix vnstat-))
+(define-maybe integer (prefix vnstat-))
+(define-maybe boolean (prefix vnstat-))
+(define-maybe alist   (prefix vnstat-))
+
+;; Documentation strings from vnstat.conf manpage adapted to texinfo.
+;; vnstat checkout: v2.10, commit b3408af1c609aa6265d296cab7bfe59a61d7cf70
+;; Do not reflow these strings or drop the initial \ escape as it makes it
+;; harder to diff against the manpage.
+(define-configuration vnstat-configuration
+  (package
+    (file-like vnstat)
+    "The vnstat package."
+    empty-serializer)
+
+  (database-dir
+   (string "/var/lib/vnstat")
+   "\
+Specifies the directory where the database is to be stored.
+A full path must be given and a leading '/' isn't required.")  
+
+  (5-minute-hours
+   (maybe-integer 48)
+   "\
+Data retention duration for the 5 minute resolution entries. The configuration
+defines for how many past hours entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (64bit-interface-counters
+   (maybe-integer -2)
+   "\
+Select interface counter handling. Set to @code{1} for defining that all interfaces
+use 64-bit counters on the kernel side and @code{0} for defining 32-bit counter. Set
+to @code{-1} for using the old style logic used in earlier versions where counter
+values within 32-bits are assumed to be 32-bit and anything larger is assumed to
+be a 64-bit counter. This may produce false results if a 64-bit counter is
+reset within the 32-bits. Set to @code{-2} for using automatic detection based on
+available kernel datastructures.")
+
+  (always-add-new-interfaces?
+   (maybe-boolean #t)
+   "\
+Enable or disable automatic creation of new database entries for interfaces not
+currently in the database even if the database file already exists when the
+daemon is started. New database entries will also get created for new interfaces
+seen while the daemon is running. Pseudo interfaces @samp{lo}, @samp{lo0} and @samp{sit0} are always
+excluded from getting added.")
+
+  (bandwidth-detection?
+   (maybe-boolean #t)
+   "\
+Try to automatically detect
+@var{max-bandwidth}
+value for each monitored interface. Mostly only ethernet interfaces support
+this feature.
+@var{max-bandwidth}
+will be used as fallback value if detection fails. Any interface specific
+@var{max-BW}
+configuration will disable the detection for the specified interface.
+In Linux, the detection is disabled for tun interfaces due to the
+Linux kernel always reporting 10 Mbit regardless of the used real interface.")
+
+  (bandwidth-detection-interval
+   (maybe-integer 5)
+   "\
+How often in minutes interface specific detection of
+@var{max-bandwidth}
+is done for detecting possible changes when
+@var{bandwidth-detection}
+is enabled. Can be disabled by setting to @code{0}. Value range: @samp{0}..@samp{30}")
+
+  (boot-variation
+   (maybe-integer 15)
+   "\
+Time in seconds how much the boot time reported by system kernel can variate
+between updates. Value range: @samp{0}..@samp{300}")
+
+  (check-disk-space?
+   (maybe-boolean #t)
+   "\
+Enable or disable the availability check of at least some free disk space before
+a database write.")
+
+  (create-dirs?
+   (maybe-boolean #t)
+   "\
+Enable or disable the creation of directories when a configured path doesn't
+exist. This includes @var{database-dir}.")
+
+  (daemon-group
+   maybe-string
+   "\
+Specify the group to which the daemon process should switch during startup.
+The group can either be the name of the group or a numerical group id.
+Leave empty to disable group switching. This option can only be used when
+the process is started as root.")
+
+  (daemon-user
+   maybe-string
+   "\
+Specify the user to which the daemon process should switch during startup.
+The user can either be the login of the user or a numerical user id.
+Leave empty to disable user switching. This option can only be used when
+the process is started as root.")
+
+  (daily-days
+   (maybe-integer 62)
+   "\
+Data retention duration for the one day resolution entries. The configuration
+defines for how many past days entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (database-synchronous
+   (maybe-integer -1)
+   "\
+Change the setting of the SQLite \"synchronous\" flag which controls how much
+care is taken to ensure disk writes have fully completed when writing data to
+the database before continuing other actions. Higher values take extra steps
+to ensure data safety at the cost of slower performance. A value of @code{0} will
+result in all handling being left to the filesystem itself. Set to @code{-1} to
+select the default value according to database mode controlled by
+@var{database-write-ahead-logging}
+setting. See SQLite documentation for more details regarding values from @code{1}
+to @code{3}. Value range: @samp{-1}..@samp{3}")
+
+  (database-write-ahead-logging?
+   (maybe-boolean #f)
+   "\
+Enable or disable SQLite Write-Ahead Logging mode for the database. See SQLite
+documentation for more details and note that support for read-only operations
+isn't available in older SQLite versions.")
+
+  (hourly-days
+   (maybe-integer 4)
+   "\
+Data retention duration for the one hour resolution entries. The configuration
+defines for how many past days entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (log-file
+   maybe-string
+   "\
+Specify log file path and name to be used if @var{use-logging} is set to @code{1}.")
+
+  (max-bandwidth
+   maybe-integer
+   "\
+Maximum bandwidth for all interfaces. If the interface specific traffic
+exceeds the given value then the data is assumed to be invalid and rejected.
+Set to 0 in order to disable the feature. Value range: @samp{0}..@samp{50000}")
+
+  ;; documentation adapted for alist type
+  (max-BW
+   maybe-alist
+   "\
+Same as
+@var{max-bandwidth}
+but can be used for setting individual limits
+for selected interfaces. This is an association list of interfaces
+as symbols/strings to integer values. For example,
+@lisp
+(max-BW
+ `((eth0 . 15000)
+   (ppp0 . 10000)))
+@end lisp
+@var{bandwidth-detection}
+is disabled on an interface specific level for each
+@var{max-BW}
+configuration. Value range: @samp{0}..@samp{50000}")
+
+  (monthly-months
+   (maybe-integer 25)
+   "\
+Data retention duration for the one month resolution entries. The configuration
+defines for how many past months entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (month-rotate
+   (maybe-integer 1)
+   "\
+Day of month that months are expected to change. Usually set to
+1 but can be set to alternative values for example for tracking
+monthly billed traffic where the billing period doesn't start on
+the first day. For example, if set to 7, days of February up to and
+including the 6th will count for January. Changing this option will
+not cause existing data to be recalculated. Value range: @samp{1}..@samp{28}")
+
+  (month-rotate-affects-years?
+   (maybe-boolean #f)
+   "\
+Enable or disable
+@var{month-rotate}
+also affecting yearly data. Applicable only when
+@var{month-rotate}
+has a value greater than one.")
+
+  (offline-save-interval
+   (maybe-integer 30)
+   "\
+How often in minutes cached interface data is saved to file when all monitored
+interfaces are offline. Value range:
+@var{save-interval}..@samp{60}")
+
+  (pid-file
+   (maybe-string "/var/run/vnstatd.pid")
+   "\
+Specify pid file path and name to be used.")
+
+  (poll-interval
+   (maybe-integer 5)
+   "\
+How often in seconds interfaces are checked for status changes.
+Value range: @samp{2}..@samp{60}")
+
+  (rescan-database-on-save?
+   maybe-boolean
+   "\
+Automatically discover added interfaces from the database and start monitoring.
+The rescan is done every
+@var{save-interval}
+or
+@var{offline-save-interval}
+minutes depending on the current activity state.")
+
+  (save-interval
+   (maybe-integer 5)
+   "\
+How often in minutes cached interface data is saved to file.
+Value range: (
+@var{update-interval} / 60 )..@samp{60}")
+
+  (save-on-status-change?
+   (maybe-boolean #t)
+   "\
+Enable or disable the additional saving to file of cached interface data
+when the availability of an interface changes, i.e., when an interface goes
+offline or comes online.")
+
+  (time-sync-wait
+   (maybe-integer 5)
+   "\
+How many minutes to wait during daemon startup for system clock to sync if
+most recent database update appears to be in the future. This may be needed
+in systems without a real-time clock (RTC) which require some time after boot
+to query and set the correct time. @code{0} = wait disabled.
+Value range: @samp{0}..@samp{60}")
+
+  (top-day-entries
+   (maybe-integer 20)
+   "\
+Data retention duration for the top day entries. The configuration
+defines how many of the past top day entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (trafficless-entries?
+   (maybe-boolean #t)
+   "\
+Create database entries even when there is no traffic during the entry's time
+period.")
+
+  (update-file-owner?
+   (maybe-boolean #t)
+   "\
+Enable or disable the update of file ownership during daemon process startup.
+During daemon startup, only database, log and pid files will be modified if the
+user or group change feature (
+@var{daemon-user}
+or
+@var{daemon-group}
+) is enabled and the files don't match the requested user or group. During manual
+database creation, this option will cause file ownership to be inherited from the
+database directory if the directory already exists. This option only has effect
+when the process is started as root or via sudo.")
+
+  (update-interval
+   (maybe-integer 20)
+   "\
+How often in seconds the interface data is updated. Value range:
+@var{poll-interval}..@samp{300}")
+
+  (use-logging
+   (maybe-integer 2)
+   "\
+Enable or disable logging. This option is ignored when the daemon is started with
+.B \"-n, --nodaemon\"
+which results in all log output being shown in terminal the daemon process is using.
+@code{0} = disabled, @code{1} = logfile and @code{2} = syslog.")
+
+  (use-UTC?
+   maybe-boolean
+   "\
+Enable or disable using UTC as timezone in the database for all entries. When
+enabled, all entries added to the database will use UTC regardless of the
+configured system timezone. When disabled, the configured system timezone
+will be used. Changing this setting will not result in already existing
+data to be modified.")
+
+  (yearly-years
+   (maybe-integer -1)
+   "\
+Data retention duration for the one year resolution entries. The configuration
+defines for how many past years entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (prefix vnstat-))
+
+(define (vnstat-serialize-configuration config)
+  (mixed-text-file
+   "vnstat.conf"
+   (serialize-configuration config vnstat-configuration-fields)))
+
+(define (vnstat-shepherd-service config)
+  (let ((config-file (vnstat-serialize-configuration config)))
+    (match-record config <vnstat-configuration> (package pid-file)
+      (shepherd-service
+       (documentation "Run vnstatd.")
+       (requirement `(networking))
+       (provision '(vnstatd))
+       (start #~(make-forkexec-constructor
+                 (list #$(file-append package "/sbin/vnstatd")
+                       "--daemon"
+                       "--config" #$config-file)
+                 #:pid-file #$pid-file))
+       (stop #~(make-kill-destructor))
+       (actions
+        (list (shepherd-configuration-action config-file)
+              (shepherd-action
+               (name 'reload)
+               (documentation "Reload vnstatd.")
+               (procedure
+                #~(lambda (pid)
+                    (if pid
+                        (begin
+                          (kill pid SIGHUP)
+                          (format #t
+                                  "Issued SIGHUP to vnstatd (PID ~a)."
+                                  pid))
+                        (format #t "vnstatd is not running.")))))))))))
+
+(define (vnstat-account-service config)
+  (match-record config <vnstat-configuration> (daemon-group daemon-user)
+    (if (every maybe-value-set? (list daemon-group daemon-user))
+        (list
+         (user-group
+          (name daemon-group)
+          (system? #t))
+         (user-account
+          (name daemon-user)
+          (group daemon-group)
+          (system? #t)
+          (home-directory "/var/empty")
+          (shell (file-append shadow "/sbin/nologin"))))
+        '())))
+
+(define vnstat-service-type
+  (service-type
+   (name 'vnstat)
+   (description "vnStat network-traffic monitor service.")
+   (extensions
+    (list (service-extension shepherd-root-service-type
+                             (compose list vnstat-shepherd-service))
+          (service-extension account-service-type
+                             vnstat-account-service)))
+   (default-value (vnstat-configuration))))
+
 \f
 ;;;
 ;;; Zabbix server
-- 
2.38.1





^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH v2] services: vnstat: Use least-authority-wrapper.
  2023-01-13 20:07 [bug#60788] [PATCH] services: Add vnstat-service-type Bruno Victal
                   ` (2 preceding siblings ...)
  2023-01-18  0:34 ` [bug#60788] [PATCH v2] " Bruno Victal
@ 2023-01-18  0:37 ` Bruno Victal
  2023-02-02 14:21 ` [bug#60788] [PATCH v3] services: Add vnstat-service-type Bruno Victal
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 27+ messages in thread
From: Bruno Victal @ 2023-01-18  0:37 UTC (permalink / raw)
  To: 60788; +Cc: Bruno Victal, maxim.cournoyer

* gnu/services/monitoring.scm (vnstat-shepherd-service): Use
least-authority-wrapper.
---

This patch SHOULD NOT be applied, this is a wip patch with least-authority-wrapper
and it doesn't seem to work (at least when launched within a VM).
I leave it here in case anyone interested wants to dig in further.


 gnu/services/monitoring.scm | 42 ++++++++++++++++++++++++++++++-------
 1 file changed, 34 insertions(+), 8 deletions(-)

diff --git a/gnu/services/monitoring.scm b/gnu/services/monitoring.scm
index 78fc49da5c..c9e4f85701 100644
--- a/gnu/services/monitoring.scm
+++ b/gnu/services/monitoring.scm
@@ -29,6 +29,9 @@ (define-module (gnu services monitoring)
   #:use-module (gnu packages monitoring)
   #:use-module (gnu packages networking)
   #:use-module (gnu system shadow)
+  #:use-module ((gnu system file-systems) #:select (file-system-mapping))
+  #:use-module (gnu build linux-container)
+  #:autoload   (guix least-authority) (least-authority-wrapper)
   #:use-module (guix gexp)
   #:use-module (guix packages)
   #:use-module (guix records)
@@ -448,7 +451,7 @@ (define-configuration vnstat-configuration
 @var{save-interval}..@samp{60}")
 
   (pid-file
-   (maybe-string "/var/run/vnstatd.pid")
+   (maybe-string "/var/run/vnstatd/vnstatd.pid")
    "\
 Specify pid file path and name to be used.")
 
@@ -558,17 +561,40 @@ (define (vnstat-serialize-configuration config)
    (serialize-configuration config vnstat-configuration-fields)))
 
 (define (vnstat-shepherd-service config)
-  (let ((config-file (vnstat-serialize-configuration config)))
-    (match-record config <vnstat-configuration> (package pid-file)
+  (match-record config <vnstat-configuration> (package database-dir pid-file use-logging)
+    (let* ((config-file (vnstat-serialize-configuration config))
+           (vnstatd (least-authority-wrapper
+                     (file-append package "/sbin/vnstatd")
+                     #:name "vnstatd"
+                     #:mappings (append (if (eqv? use-logging 2)
+                                            (list (file-system-mapping
+                                                   (source "/dev/log") ; for syslog
+                                                   (target source)))
+                                            '())
+                                        (list (file-system-mapping
+                                               (source database-dir)
+                                               (target source)
+                                               (writable? #t))
+                                              (file-system-mapping
+                                               (source (dirname pid-file))
+                                               (target source)
+                                               (writable? #t))
+                                              (file-system-mapping
+                                               (source config-file)
+                                               (target source))))
+                     #:namespaces (delq 'net %namespaces))))
       (shepherd-service
        (documentation "Run vnstatd.")
        (requirement `(networking))
        (provision '(vnstatd))
-       (start #~(make-forkexec-constructor
-                 (list #$(file-append package "/sbin/vnstatd")
-                       "--daemon"
-                       "--config" #$config-file)
-                 #:pid-file #$pid-file))
+       (start #~(begin
+                  (mkdir-p #$database-dir)
+                  (mkdir-p (dirname #$pid-file))
+                  (make-forkexec-constructor
+                   (list #$vnstatd
+                         "--daemon"
+                         "--config" #$config-file)
+                   #:pid-file #$pid-file)))
        (stop #~(make-kill-destructor))
        (actions
         (list (shepherd-configuration-action config-file)
-- 
2.38.1





^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH v3] services: Add vnstat-service-type.
  2023-01-13 20:07 [bug#60788] [PATCH] services: Add vnstat-service-type Bruno Victal
                   ` (3 preceding siblings ...)
  2023-01-18  0:37 ` [bug#60788] [PATCH v2] services: vnstat: Use least-authority-wrapper Bruno Victal
@ 2023-02-02 14:21 ` Bruno Victal
  2023-02-07 14:25 ` [bug#60788] [PATCH v4] " Bruno Victal
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 27+ messages in thread
From: Bruno Victal @ 2023-02-02 14:21 UTC (permalink / raw)
  To: 60788; +Cc: Bruno Victal, maxim.cournoyer

* gnu/services/monitoring.scm (vnstat-service-type): New variable.
* doc/guix.texi (Monitoring Services): Document it.
---

Changes since v2:
  * Export accessors.

 doc/guix.texi               | 238 +++++++++++++++++++
 gnu/services/monitoring.scm | 451 ++++++++++++++++++++++++++++++++++++
 2 files changed, 689 insertions(+)

diff --git a/doc/guix.texi b/doc/guix.texi
index 64873db00b..cb2c1c3c13 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -112,6 +112,7 @@
 Copyright @copyright{} 2022 Ivan Vilata-i-Balaguer@*
 Copyright @copyright{} 2023 Giacomo Leidi@*
 Copyright @copyright{} 2022 Antero Mejr@*
+Copyright @copyright{} 2023 Bruno Victal@*
 
 Permission is granted to copy, distribute and/or modify this document
 under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -28267,6 +28268,243 @@ Monitoring Services
 @end table
 @end deftp
 
+@anchor{vnstat}
+@subsubheading vnStat Network Traffic Monitor
+@cindex vnstat
+
+vnStat is a network traffic monitor that uses interface statistics provided
+by the kernel rather than traffic sniffing.  This makes it a light resource
+monitor, regardless of network traffic rate.
+
+@defvar vnstat-service-type
+This is the service type for the @uref{https://humdi.net/vnstat/,vnStat} daemon
+and accepts a @code{vnstat-configuration} value.
+@end defvar
+
+@c %start of fragment
+@deftp {Data Type} vnstat-configuration
+Available @code{vnstat-configuration} fields are:
+
+@table @asis
+@item @code{package} (default: @code{vnstat}) (type: file-like)
+The vnstat package.
+
+@item @code{database-dir} (default: @code{"/var/lib/vnstat"}) (type: string)
+Specifies the directory where the database is to be stored.  A full path
+must be given and a leading '/' isn't required.
+
+@item @code{5-minute-hours} (default: @code{48}) (type: maybe-integer)
+Data retention duration for the 5 minute resolution entries.  The
+configuration defines for how many past hours entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@item @code{64bit-interface-counters} (default: @code{-2}) (type: maybe-integer)
+Select interface counter handling.  Set to @code{1} for defining that
+all interfaces use 64-bit counters on the kernel side and @code{0} for
+defining 32-bit counter.  Set to @code{-1} for using the old style logic
+used in earlier versions where counter values within 32-bits are assumed
+to be 32-bit and anything larger is assumed to be a 64-bit counter.  This
+may produce false results if a 64-bit counter is reset within the
+32-bits.  Set to @code{-2} for using automatic detection based on
+available kernel datastructures.
+
+@item @code{always-add-new-interfaces?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable automatic creation of new database entries for
+interfaces not currently in the database even if the database file
+already exists when the daemon is started.  New database entries will
+also get created for new interfaces seen while the daemon is running.
+Pseudo interfaces @samp{lo}, @samp{lo0} and @samp{sit0} are always excluded from getting
+added.
+
+@item @code{bandwidth-detection?} (default: @code{#t}) (type: maybe-boolean)
+Try to automatically detect @var{max-bandwidth} value for each monitored
+interface.  Mostly only ethernet interfaces support this feature.
+@var{max-bandwidth} will be used as fallback value if detection fails.
+Any interface specific @var{max-BW} configuration will disable the
+detection for the specified interface.  In Linux, the detection is
+disabled for tun interfaces due to the Linux kernel always reporting 10
+Mbit regardless of the used real interface.
+
+@item @code{bandwidth-detection-interval} (default: @code{5}) (type: maybe-integer)
+How often in minutes interface specific detection of @var{max-bandwidth}
+is done for detecting possible changes when @var{bandwidth-detection} is
+enabled.  Can be disabled by setting to @code{0}.  Value range:
+@samp{0}..@samp{30}
+
+@item @code{boot-variation} (default: @code{15}) (type: maybe-integer)
+Time in seconds how much the boot time reported by system kernel can
+variate between updates.  Value range: @samp{0}..@samp{300}
+
+@item @code{check-disk-space?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the availability check of at least some free disk
+space before a database write.
+
+@item @code{create-dirs?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the creation of directories when a configured path
+doesn't exist.  This includes @var{database-dir}.
+
+@item @code{daemon-group} (type: maybe-string)
+Specify the group to which the daemon process should switch during
+startup.  The group can either be the name of the group or a numerical
+group id.  Leave empty to disable group switching.  This option can only
+be used when the process is started as root.
+
+@item @code{daemon-user} (type: maybe-string)
+Specify the user to which the daemon process should switch during
+startup.  The user can either be the login of the user or a numerical
+user id.  Leave empty to disable user switching.  This option can only
+be used when the process is started as root.
+
+@item @code{daily-days} (default: @code{62}) (type: maybe-integer)
+Data retention duration for the one day resolution entries.  The
+configuration defines for how many past days entries will be stored.  Set
+to @code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{database-synchronous} (default: @code{-1}) (type: maybe-integer)
+Change the setting of the SQLite "synchronous" flag which controls how
+much care is taken to ensure disk writes have fully completed when
+writing data to the database before continuing other actions.  Higher
+values take extra steps to ensure data safety at the cost of slower
+performance.  A value of @code{0} will result in all handling being left
+to the filesystem itself.  Set to @code{-1} to select the default value
+according to database mode controlled by
+@var{database-write-ahead-logging} setting.  See SQLite documentation
+for more details regarding values from @code{1} to @code{3}.  Value
+range: @samp{-1}..@samp{3}
+
+@item @code{database-write-ahead-logging?} (default: @code{#f}) (type: maybe-boolean)
+Enable or disable SQLite Write-Ahead Logging mode for the database.  See
+SQLite documentation for more details and note that support for
+read-only operations isn't available in older SQLite versions.
+
+@item @code{hourly-days} (default: @code{4}) (type: maybe-integer)
+Data retention duration for the one hour resolution entries.  The
+configuration defines for how many past days entries will be stored.  Set
+to @code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{log-file} (type: maybe-string)
+Specify log file path and name to be used if @var{use-logging} is set to
+@code{1}.
+
+@item @code{max-bandwidth} (type: maybe-integer)
+Maximum bandwidth for all interfaces.  If the interface specific traffic
+exceeds the given value then the data is assumed to be invalid and
+rejected.  Set to 0 in order to disable the feature.  Value range:
+@samp{0}..@samp{50000}
+
+@item @code{max-BW} (type: maybe-alist)
+Same as @var{max-bandwidth} but can be used for setting individual
+limits for selected interfaces.  This is an association list of
+interfaces as symbols/strings to integer values.  For example,
+@lisp
+(max-BW `((eth0 .  15000)
+          (ppp0 .  10000)))
+@end lisp
+@var{bandwidth-detection} is disabled on an interface specific level for
+each @var{max-BW} configuration.  Value range: @samp{0}..@samp{50000}
+
+@item @code{monthly-months} (default: @code{25}) (type: maybe-integer)
+Data retention duration for the one month resolution entries.  The
+configuration defines for how many past months entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@item @code{month-rotate} (default: @code{1}) (type: maybe-integer)
+Day of month that months are expected to change.  Usually set to 1 but
+can be set to alternative values for example for tracking monthly billed
+traffic where the billing period doesn't start on the first day.  For
+example, if set to 7, days of February up to and including the 6th will
+count for January.  Changing this option will not cause existing data to
+be recalculated.  Value range: @samp{1}..@samp{28}
+
+@item @code{month-rotate-affects-years?} (default: @code{#f}) (type: maybe-boolean)
+Enable or disable @var{month-rotate} also affecting yearly data.
+Applicable only when @var{month-rotate} has a value greater than one.
+
+@item @code{offline-save-interval} (default: @code{30}) (type: maybe-integer)
+How often in minutes cached interface data is saved to file when all
+monitored interfaces are offline.  Value range:
+@var{save-interval}..@samp{60}
+
+@item @code{pid-file} (default: @code{"/var/run/vnstatd.pid"}) (type: maybe-string)
+Specify pid file path and name to be used.
+
+@item @code{poll-interval} (default: @code{5}) (type: maybe-integer)
+How often in seconds interfaces are checked for status changes.  Value
+range: @samp{2}..@samp{60}
+
+@item @code{rescan-database-on-save?} (type: maybe-boolean)
+Automatically discover added interfaces from the database and start
+monitoring.  The rescan is done every @var{save-interval} or
+@var{offline-save-interval} minutes depending on the current activity
+state.
+
+@item @code{save-interval} (default: @code{5}) (type: maybe-integer)
+How often in minutes cached interface data is saved to file.  Value
+range: ( @var{update-interval} / 60 )..@samp{60}
+
+@item @code{save-on-status-change?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the additional saving to file of cached interface data
+when the availability of an interface changes, i.e., when an interface
+goes offline or comes online.
+
+@item @code{time-sync-wait} (default: @code{5}) (type: maybe-integer)
+How many minutes to wait during daemon startup for system clock to sync
+if most recent database update appears to be in the future.  This may be
+needed in systems without a real-time clock (RTC) which require some
+time after boot to query and set the correct time.  @code{0} = wait
+disabled.  Value range: @samp{0}..@samp{60}
+
+@item @code{top-day-entries} (default: @code{20}) (type: maybe-integer)
+Data retention duration for the top day entries.  The configuration
+defines how many of the past top day entries will be stored.  Set to
+@code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{trafficless-entries?} (default: @code{#t}) (type: maybe-boolean)
+Create database entries even when there is no traffic during the entry's
+time period.
+
+@item @code{update-file-owner?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the update of file ownership during daemon process
+startup.  During daemon startup, only database, log and pid files will
+be modified if the user or group change feature ( @var{daemon-user} or
+@var{daemon-group} ) is enabled and the files don't match the requested
+user or group.  During manual database creation, this option will cause
+file ownership to be inherited from the database directory if the
+directory already exists.  This option only has effect when the process
+is started as root or via sudo.
+
+@item @code{update-interval} (default: @code{20}) (type: maybe-integer)
+How often in seconds the interface data is updated.  Value range:
+@var{poll-interval}..@samp{300}
+
+@item @code{use-logging} (default: @code{2}) (type: maybe-integer)
+Enable or disable logging.  This option is ignored when the daemon is
+started with .B "-n, --nodaemon" which results in all log output being
+shown in terminal the daemon process is using.  @code{0} = disabled,
+@code{1} = logfile and @code{2} = syslog.
+
+@item @code{use-UTC?} (type: maybe-boolean)
+Enable or disable using UTC as timezone in the database for all entries.
+When enabled, all entries added to the database will use UTC regardless
+of the configured system timezone.  When disabled, the configured system
+timezone will be used.  Changing this setting will not result in already
+existing data to be modified.
+
+@item @code{yearly-years} (default: @code{-1}) (type: maybe-integer)
+Data retention duration for the one year resolution entries.  The
+configuration defines for how many past years entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@end table
+@end deftp
+@c %end of fragment
+
 @subsubheading Zabbix server
 @cindex zabbix zabbix-server
 Zabbix is a high performance monitoring system that can collect data from a
diff --git a/gnu/services/monitoring.scm b/gnu/services/monitoring.scm
index 44e2e8886c..870943e80b 100644
--- a/gnu/services/monitoring.scm
+++ b/gnu/services/monitoring.scm
@@ -3,6 +3,7 @@
 ;;; Copyright © 2018, 2019 Gábor Boskovits <boskovits@gmail.com>
 ;;; Copyright © 2018, 2019, 2020 Oleg Pykhalov <go.wigust@gmail.com>
 ;;; Copyright © 2022 Marius Bakke <marius@gnu.org>
+;;; Copyright © 2023 Bruno Victal <mirai@makinata.eu>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -26,6 +27,7 @@ (define-module (gnu services monitoring)
   #:use-module (gnu services web)
   #:use-module (gnu packages admin)
   #:use-module (gnu packages monitoring)
+  #:use-module (gnu packages networking)
   #:use-module (gnu system shadow)
   #:use-module (guix gexp)
   #:use-module (guix packages)
@@ -34,6 +36,7 @@ (define-module (gnu services monitoring)
   #:use-module ((guix ui) #:select (display-hint G_))
   #:use-module (ice-9 match)
   #:use-module (ice-9 rdelim)
+  #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-26)
   #:use-module (srfi srfi-35)
   #:export (darkstat-configuration
@@ -45,6 +48,46 @@ (define-module (gnu services monitoring)
             prometheus-node-exporter-web-listen-address
             prometheus-node-exporter-service-type
 
+            vnstat-configuration
+            vnstat-configuration?
+            vnstat-service-type
+            vnstat-configuration-package
+            vnstat-configuration-database-dir
+            vnstat-configuration-5-minute-hours
+            vnstat-configuration-64bit-interface-counters
+            vnstat-configuration-always-add-new-interfaces?
+            vnstat-configuration-bandwidth-detection?
+            vnstat-configuration-bandwidth-detection-interval
+            vnstat-configuration-boot-variation
+            vnstat-configuration-check-disk-space?
+            vnstat-configuration-create-dirs?
+            vnstat-configuration-daemon-group
+            vnstat-configuration-daemon-user
+            vnstat-configuration-daily-days
+            vnstat-configuration-database-synchronous
+            vnstat-configuration-database-write-ahead-logging?
+            vnstat-configuration-hourly-days
+            vnstat-configuration-log-file
+            vnstat-configuration-max-bandwidth
+            vnstat-configuration-max-BW
+            vnstat-configuration-monthly-months
+            vnstat-configuration-month-rotate
+            vnstat-configuration-month-rotate-affects-years?
+            vnstat-configuration-offline-save-interval
+            vnstat-configuration-pid-file
+            vnstat-configuration-poll-interval
+            vnstat-configuration-rescan-database-on-save?
+            vnstat-configuration-save-interval
+            vnstat-configuration-save-on-status-change?
+            vnstat-configuration-time-sync-wait
+            vnstat-configuration-top-day-entries
+            vnstat-configuration-trafficless-entries?
+            vnstat-configuration-update-file-owner?
+            vnstat-configuration-update-interval
+            vnstat-configuration-use-logging
+            vnstat-configuration-use-UTC?
+            vnstat-configuration-yearly-years
+
             zabbix-server-configuration
             zabbix-server-service-type
             zabbix-agent-configuration
@@ -196,6 +239,414 @@ (define prometheus-node-exporter-service-type
                         prometheus-node-exporter-shepherd-service)))
    (default-value (prometheus-node-exporter-configuration))))
 
+\f
+;;;
+;;; vnstat daemon
+;;;
+
+(define* (camelfy-field-name field-name #:key (dromedary? #f))
+  (match (string-split (symbol->string field-name) #\-)
+    ((head tail ...)
+     (string-join (cons (if dromedary? head (string-upcase head 0 1))
+                        (map (cut string-upcase <> 0 1) tail)) ""))))
+
+(define (strip-trailing-?-character field-name)
+  "Drop rightmost '?' character"
+  (let ((str (symbol->string field-name)))
+    (if (string-suffix? "?" str)
+        (string->symbol (string-drop-right str 1))
+        field-name)))
+
+(define (vnstat-serialize-string field-name value)
+  #~(format #f "~a ~s~%"
+            #$(camelfy-field-name field-name)
+            #$value))
+
+(define vnstat-serialize-integer vnstat-serialize-string)
+
+(define (vnstat-serialize-boolean field-name value)
+  #~(format #f "~a ~a~%"
+            #$(camelfy-field-name (strip-trailing-?-character field-name))
+            #$(if value 1 0)))
+
+(define (vnstat-serialize-alist field-name value)
+  (generic-serialize-alist string-append
+                           (lambda (iface val)
+                             (vnstat-serialize-integer
+                              (format #f "MaxBW~a" iface) val))
+                           value))
+
+(define-maybe string  (prefix vnstat-))
+(define-maybe integer (prefix vnstat-))
+(define-maybe boolean (prefix vnstat-))
+(define-maybe alist   (prefix vnstat-))
+
+;; Documentation strings from vnstat.conf manpage adapted to texinfo.
+;; vnstat checkout: v2.10, commit b3408af1c609aa6265d296cab7bfe59a61d7cf70
+;; Do not reflow these strings or drop the initial \ escape as it makes it
+;; harder to diff against the manpage.
+(define-configuration vnstat-configuration
+  (package
+    (file-like vnstat)
+    "The vnstat package."
+    empty-serializer)
+
+  (database-dir
+   (string "/var/lib/vnstat")
+   "\
+Specifies the directory where the database is to be stored.
+A full path must be given and a leading '/' isn't required.")  
+
+  (5-minute-hours
+   (maybe-integer 48)
+   "\
+Data retention duration for the 5 minute resolution entries. The configuration
+defines for how many past hours entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (64bit-interface-counters
+   (maybe-integer -2)
+   "\
+Select interface counter handling. Set to @code{1} for defining that all interfaces
+use 64-bit counters on the kernel side and @code{0} for defining 32-bit counter. Set
+to @code{-1} for using the old style logic used in earlier versions where counter
+values within 32-bits are assumed to be 32-bit and anything larger is assumed to
+be a 64-bit counter. This may produce false results if a 64-bit counter is
+reset within the 32-bits. Set to @code{-2} for using automatic detection based on
+available kernel datastructures.")
+
+  (always-add-new-interfaces?
+   (maybe-boolean #t)
+   "\
+Enable or disable automatic creation of new database entries for interfaces not
+currently in the database even if the database file already exists when the
+daemon is started. New database entries will also get created for new interfaces
+seen while the daemon is running. Pseudo interfaces @samp{lo}, @samp{lo0} and @samp{sit0} are always
+excluded from getting added.")
+
+  (bandwidth-detection?
+   (maybe-boolean #t)
+   "\
+Try to automatically detect
+@var{max-bandwidth}
+value for each monitored interface. Mostly only ethernet interfaces support
+this feature.
+@var{max-bandwidth}
+will be used as fallback value if detection fails. Any interface specific
+@var{max-BW}
+configuration will disable the detection for the specified interface.
+In Linux, the detection is disabled for tun interfaces due to the
+Linux kernel always reporting 10 Mbit regardless of the used real interface.")
+
+  (bandwidth-detection-interval
+   (maybe-integer 5)
+   "\
+How often in minutes interface specific detection of
+@var{max-bandwidth}
+is done for detecting possible changes when
+@var{bandwidth-detection}
+is enabled. Can be disabled by setting to @code{0}. Value range: @samp{0}..@samp{30}")
+
+  (boot-variation
+   (maybe-integer 15)
+   "\
+Time in seconds how much the boot time reported by system kernel can variate
+between updates. Value range: @samp{0}..@samp{300}")
+
+  (check-disk-space?
+   (maybe-boolean #t)
+   "\
+Enable or disable the availability check of at least some free disk space before
+a database write.")
+
+  (create-dirs?
+   (maybe-boolean #t)
+   "\
+Enable or disable the creation of directories when a configured path doesn't
+exist. This includes @var{database-dir}.")
+
+  (daemon-group
+   maybe-string
+   "\
+Specify the group to which the daemon process should switch during startup.
+The group can either be the name of the group or a numerical group id.
+Leave empty to disable group switching. This option can only be used when
+the process is started as root.")
+
+  (daemon-user
+   maybe-string
+   "\
+Specify the user to which the daemon process should switch during startup.
+The user can either be the login of the user or a numerical user id.
+Leave empty to disable user switching. This option can only be used when
+the process is started as root.")
+
+  (daily-days
+   (maybe-integer 62)
+   "\
+Data retention duration for the one day resolution entries. The configuration
+defines for how many past days entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (database-synchronous
+   (maybe-integer -1)
+   "\
+Change the setting of the SQLite \"synchronous\" flag which controls how much
+care is taken to ensure disk writes have fully completed when writing data to
+the database before continuing other actions. Higher values take extra steps
+to ensure data safety at the cost of slower performance. A value of @code{0} will
+result in all handling being left to the filesystem itself. Set to @code{-1} to
+select the default value according to database mode controlled by
+@var{database-write-ahead-logging}
+setting. See SQLite documentation for more details regarding values from @code{1}
+to @code{3}. Value range: @samp{-1}..@samp{3}")
+
+  (database-write-ahead-logging?
+   (maybe-boolean #f)
+   "\
+Enable or disable SQLite Write-Ahead Logging mode for the database. See SQLite
+documentation for more details and note that support for read-only operations
+isn't available in older SQLite versions.")
+
+  (hourly-days
+   (maybe-integer 4)
+   "\
+Data retention duration for the one hour resolution entries. The configuration
+defines for how many past days entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (log-file
+   maybe-string
+   "\
+Specify log file path and name to be used if @var{use-logging} is set to @code{1}.")
+
+  (max-bandwidth
+   maybe-integer
+   "\
+Maximum bandwidth for all interfaces. If the interface specific traffic
+exceeds the given value then the data is assumed to be invalid and rejected.
+Set to 0 in order to disable the feature. Value range: @samp{0}..@samp{50000}")
+
+  ;; documentation adapted for alist type
+  (max-BW
+   maybe-alist
+   "\
+Same as
+@var{max-bandwidth}
+but can be used for setting individual limits
+for selected interfaces. This is an association list of interfaces
+as symbols/strings to integer values. For example,
+@lisp
+(max-BW
+ `((eth0 . 15000)
+   (ppp0 . 10000)))
+@end lisp
+@var{bandwidth-detection}
+is disabled on an interface specific level for each
+@var{max-BW}
+configuration. Value range: @samp{0}..@samp{50000}")
+
+  (monthly-months
+   (maybe-integer 25)
+   "\
+Data retention duration for the one month resolution entries. The configuration
+defines for how many past months entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (month-rotate
+   (maybe-integer 1)
+   "\
+Day of month that months are expected to change. Usually set to
+1 but can be set to alternative values for example for tracking
+monthly billed traffic where the billing period doesn't start on
+the first day. For example, if set to 7, days of February up to and
+including the 6th will count for January. Changing this option will
+not cause existing data to be recalculated. Value range: @samp{1}..@samp{28}")
+
+  (month-rotate-affects-years?
+   (maybe-boolean #f)
+   "\
+Enable or disable
+@var{month-rotate}
+also affecting yearly data. Applicable only when
+@var{month-rotate}
+has a value greater than one.")
+
+  (offline-save-interval
+   (maybe-integer 30)
+   "\
+How often in minutes cached interface data is saved to file when all monitored
+interfaces are offline. Value range:
+@var{save-interval}..@samp{60}")
+
+  (pid-file
+   (maybe-string "/var/run/vnstatd.pid")
+   "\
+Specify pid file path and name to be used.")
+
+  (poll-interval
+   (maybe-integer 5)
+   "\
+How often in seconds interfaces are checked for status changes.
+Value range: @samp{2}..@samp{60}")
+
+  (rescan-database-on-save?
+   maybe-boolean
+   "\
+Automatically discover added interfaces from the database and start monitoring.
+The rescan is done every
+@var{save-interval}
+or
+@var{offline-save-interval}
+minutes depending on the current activity state.")
+
+  (save-interval
+   (maybe-integer 5)
+   "\
+How often in minutes cached interface data is saved to file.
+Value range: (
+@var{update-interval} / 60 )..@samp{60}")
+
+  (save-on-status-change?
+   (maybe-boolean #t)
+   "\
+Enable or disable the additional saving to file of cached interface data
+when the availability of an interface changes, i.e., when an interface goes
+offline or comes online.")
+
+  (time-sync-wait
+   (maybe-integer 5)
+   "\
+How many minutes to wait during daemon startup for system clock to sync if
+most recent database update appears to be in the future. This may be needed
+in systems without a real-time clock (RTC) which require some time after boot
+to query and set the correct time. @code{0} = wait disabled.
+Value range: @samp{0}..@samp{60}")
+
+  (top-day-entries
+   (maybe-integer 20)
+   "\
+Data retention duration for the top day entries. The configuration
+defines how many of the past top day entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (trafficless-entries?
+   (maybe-boolean #t)
+   "\
+Create database entries even when there is no traffic during the entry's time
+period.")
+
+  (update-file-owner?
+   (maybe-boolean #t)
+   "\
+Enable or disable the update of file ownership during daemon process startup.
+During daemon startup, only database, log and pid files will be modified if the
+user or group change feature (
+@var{daemon-user}
+or
+@var{daemon-group}
+) is enabled and the files don't match the requested user or group. During manual
+database creation, this option will cause file ownership to be inherited from the
+database directory if the directory already exists. This option only has effect
+when the process is started as root or via sudo.")
+
+  (update-interval
+   (maybe-integer 20)
+   "\
+How often in seconds the interface data is updated. Value range:
+@var{poll-interval}..@samp{300}")
+
+  (use-logging
+   (maybe-integer 2)
+   "\
+Enable or disable logging. This option is ignored when the daemon is started with
+.B \"-n, --nodaemon\"
+which results in all log output being shown in terminal the daemon process is using.
+@code{0} = disabled, @code{1} = logfile and @code{2} = syslog.")
+
+  (use-UTC?
+   maybe-boolean
+   "\
+Enable or disable using UTC as timezone in the database for all entries. When
+enabled, all entries added to the database will use UTC regardless of the
+configured system timezone. When disabled, the configured system timezone
+will be used. Changing this setting will not result in already existing
+data to be modified.")
+
+  (yearly-years
+   (maybe-integer -1)
+   "\
+Data retention duration for the one year resolution entries. The configuration
+defines for how many past years entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (prefix vnstat-))
+
+(define (vnstat-serialize-configuration config)
+  (mixed-text-file
+   "vnstat.conf"
+   (serialize-configuration config vnstat-configuration-fields)))
+
+(define (vnstat-shepherd-service config)
+  (let ((config-file (vnstat-serialize-configuration config)))
+    (match-record config <vnstat-configuration> (package pid-file)
+      (shepherd-service
+       (documentation "Run vnstatd.")
+       (requirement `(networking))
+       (provision '(vnstatd))
+       (start #~(make-forkexec-constructor
+                 (list #$(file-append package "/sbin/vnstatd")
+                       "--daemon"
+                       "--config" #$config-file)
+                 #:pid-file #$pid-file))
+       (stop #~(make-kill-destructor))
+       (actions
+        (list (shepherd-configuration-action config-file)
+              (shepherd-action
+               (name 'reload)
+               (documentation "Reload vnstatd.")
+               (procedure
+                #~(lambda (pid)
+                    (if pid
+                        (begin
+                          (kill pid SIGHUP)
+                          (format #t
+                                  "Issued SIGHUP to vnstatd (PID ~a)."
+                                  pid))
+                        (format #t "vnstatd is not running.")))))))))))
+
+(define (vnstat-account-service config)
+  (match-record config <vnstat-configuration> (daemon-group daemon-user)
+    (if (every maybe-value-set? (list daemon-group daemon-user))
+        (list
+         (user-group
+          (name daemon-group)
+          (system? #t))
+         (user-account
+          (name daemon-user)
+          (group daemon-group)
+          (system? #t)
+          (home-directory "/var/empty")
+          (shell (file-append shadow "/sbin/nologin"))))
+        '())))
+
+(define vnstat-service-type
+  (service-type
+   (name 'vnstat)
+   (description "vnStat network-traffic monitor service.")
+   (extensions
+    (list (service-extension shepherd-root-service-type
+                             (compose list vnstat-shepherd-service))
+          (service-extension account-service-type
+                             vnstat-account-service)))
+   (default-value (vnstat-configuration))))
+
 \f
 ;;;
 ;;; Zabbix server

base-commit: f5446f721121c50a6e8fdf1e7285d861dfd180b8
-- 
2.38.1





^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH v4] services: Add vnstat-service-type.
  2023-01-13 20:07 [bug#60788] [PATCH] services: Add vnstat-service-type Bruno Victal
                   ` (4 preceding siblings ...)
  2023-02-02 14:21 ` [bug#60788] [PATCH v3] services: Add vnstat-service-type Bruno Victal
@ 2023-02-07 14:25 ` Bruno Victal
  2023-02-09  3:34   ` [bug#60788] [PATCH] " Maxim Cournoyer
  2023-02-10 13:15 ` [bug#60788] [PATCH v5] " Bruno Victal
                   ` (7 subsequent siblings)
  13 siblings, 1 reply; 27+ messages in thread
From: Bruno Victal @ 2023-02-07 14:25 UTC (permalink / raw)
  To: 60788; +Cc: Bruno Victal, maxim.cournoyer

* gnu/services/monitoring.scm (vnstat-service-type): New variable.
* doc/guix.texi (Monitoring Services): Document it.
---
 doc/guix.texi               | 240 ++++++++++++++++++-
 gnu/services/monitoring.scm | 451 ++++++++++++++++++++++++++++++++++++
 2 files changed, 689 insertions(+), 2 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index 359c9b7a47..7779aadd83 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -109,11 +109,10 @@
 Copyright @copyright{} 2022 Simon Streit@*
 Copyright @copyright{} 2022 (@*
 Copyright @copyright{} 2022 John Kehayias@*
-Copyright @copyright{} 2022 Bruno Victal@*
 Copyright @copyright{} 2022 Ivan Vilata-i-Balaguer@*
 Copyright @copyright{} 2023 Giacomo Leidi@*
 Copyright @copyright{} 2022 Antero Mejr@*
-Copyright @copyright{} 2022 Bruno Victal@*
+Copyright @copyright{} 2022⁠–⁠2023 Bruno Victal@*
 
 Permission is granted to copy, distribute and/or modify this document
 under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -28269,6 +28268,243 @@ Monitoring Services
 @end table
 @end deftp
 
+@anchor{vnstat}
+@subsubheading vnStat Network Traffic Monitor
+@cindex vnstat
+
+vnStat is a network traffic monitor that uses interface statistics provided
+by the kernel rather than traffic sniffing.  This makes it a light resource
+monitor, regardless of network traffic rate.
+
+@defvar vnstat-service-type
+This is the service type for the @uref{https://humdi.net/vnstat/,vnStat} daemon
+and accepts a @code{vnstat-configuration} value.
+@end defvar
+
+@c %start of fragment
+@deftp {Data Type} vnstat-configuration
+Available @code{vnstat-configuration} fields are:
+
+@table @asis
+@item @code{package} (default: @code{vnstat}) (type: file-like)
+The vnstat package.
+
+@item @code{database-dir} (default: @code{"/var/lib/vnstat"}) (type: string)
+Specifies the directory where the database is to be stored.  A full path
+must be given and a leading '/' isn't required.
+
+@item @code{5-minute-hours} (default: @code{48}) (type: maybe-integer)
+Data retention duration for the 5 minute resolution entries.  The
+configuration defines for how many past hours entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@item @code{64bit-interface-counters} (default: @code{-2}) (type: maybe-integer)
+Select interface counter handling.  Set to @code{1} for defining that
+all interfaces use 64-bit counters on the kernel side and @code{0} for
+defining 32-bit counter.  Set to @code{-1} for using the old style logic
+used in earlier versions where counter values within 32-bits are assumed
+to be 32-bit and anything larger is assumed to be a 64-bit counter.  This
+may produce false results if a 64-bit counter is reset within the
+32-bits.  Set to @code{-2} for using automatic detection based on
+available kernel datastructures.
+
+@item @code{always-add-new-interfaces?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable automatic creation of new database entries for
+interfaces not currently in the database even if the database file
+already exists when the daemon is started.  New database entries will
+also get created for new interfaces seen while the daemon is running.
+Pseudo interfaces @samp{lo}, @samp{lo0} and @samp{sit0} are always excluded from getting
+added.
+
+@item @code{bandwidth-detection?} (default: @code{#t}) (type: maybe-boolean)
+Try to automatically detect @var{max-bandwidth} value for each monitored
+interface.  Mostly only ethernet interfaces support this feature.
+@var{max-bandwidth} will be used as fallback value if detection fails.
+Any interface specific @var{max-BW} configuration will disable the
+detection for the specified interface.  In Linux, the detection is
+disabled for tun interfaces due to the Linux kernel always reporting 10
+Mbit regardless of the used real interface.
+
+@item @code{bandwidth-detection-interval} (default: @code{5}) (type: maybe-integer)
+How often in minutes interface specific detection of @var{max-bandwidth}
+is done for detecting possible changes when @var{bandwidth-detection} is
+enabled.  Can be disabled by setting to @code{0}.  Value range:
+@samp{0}..@samp{30}
+
+@item @code{boot-variation} (default: @code{15}) (type: maybe-integer)
+Time in seconds how much the boot time reported by system kernel can
+variate between updates.  Value range: @samp{0}..@samp{300}
+
+@item @code{check-disk-space?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the availability check of at least some free disk
+space before a database write.
+
+@item @code{create-dirs?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the creation of directories when a configured path
+doesn't exist.  This includes @var{database-dir}.
+
+@item @code{daemon-group} (type: maybe-string)
+Specify the group to which the daemon process should switch during
+startup.  The group can either be the name of the group or a numerical
+group id.  Leave empty to disable group switching.  This option can only
+be used when the process is started as root.
+
+@item @code{daemon-user} (type: maybe-string)
+Specify the user to which the daemon process should switch during
+startup.  The user can either be the login of the user or a numerical
+user id.  Leave empty to disable user switching.  This option can only
+be used when the process is started as root.
+
+@item @code{daily-days} (default: @code{62}) (type: maybe-integer)
+Data retention duration for the one day resolution entries.  The
+configuration defines for how many past days entries will be stored.  Set
+to @code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{database-synchronous} (default: @code{-1}) (type: maybe-integer)
+Change the setting of the SQLite "synchronous" flag which controls how
+much care is taken to ensure disk writes have fully completed when
+writing data to the database before continuing other actions.  Higher
+values take extra steps to ensure data safety at the cost of slower
+performance.  A value of @code{0} will result in all handling being left
+to the filesystem itself.  Set to @code{-1} to select the default value
+according to database mode controlled by
+@var{database-write-ahead-logging} setting.  See SQLite documentation
+for more details regarding values from @code{1} to @code{3}.  Value
+range: @samp{-1}..@samp{3}
+
+@item @code{database-write-ahead-logging?} (default: @code{#f}) (type: maybe-boolean)
+Enable or disable SQLite Write-Ahead Logging mode for the database.  See
+SQLite documentation for more details and note that support for
+read-only operations isn't available in older SQLite versions.
+
+@item @code{hourly-days} (default: @code{4}) (type: maybe-integer)
+Data retention duration for the one hour resolution entries.  The
+configuration defines for how many past days entries will be stored.  Set
+to @code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{log-file} (type: maybe-string)
+Specify log file path and name to be used if @var{use-logging} is set to
+@code{1}.
+
+@item @code{max-bandwidth} (type: maybe-integer)
+Maximum bandwidth for all interfaces.  If the interface specific traffic
+exceeds the given value then the data is assumed to be invalid and
+rejected.  Set to 0 in order to disable the feature.  Value range:
+@samp{0}..@samp{50000}
+
+@item @code{max-BW} (type: maybe-alist)
+Same as @var{max-bandwidth} but can be used for setting individual
+limits for selected interfaces.  This is an association list of
+interfaces as symbols/strings to integer values.  For example,
+@lisp
+(max-BW `((eth0 .  15000)
+          (ppp0 .  10000)))
+@end lisp
+@var{bandwidth-detection} is disabled on an interface specific level for
+each @var{max-BW} configuration.  Value range: @samp{0}..@samp{50000}
+
+@item @code{monthly-months} (default: @code{25}) (type: maybe-integer)
+Data retention duration for the one month resolution entries.  The
+configuration defines for how many past months entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@item @code{month-rotate} (default: @code{1}) (type: maybe-integer)
+Day of month that months are expected to change.  Usually set to 1 but
+can be set to alternative values for example for tracking monthly billed
+traffic where the billing period doesn't start on the first day.  For
+example, if set to 7, days of February up to and including the 6th will
+count for January.  Changing this option will not cause existing data to
+be recalculated.  Value range: @samp{1}..@samp{28}
+
+@item @code{month-rotate-affects-years?} (default: @code{#f}) (type: maybe-boolean)
+Enable or disable @var{month-rotate} also affecting yearly data.
+Applicable only when @var{month-rotate} has a value greater than one.
+
+@item @code{offline-save-interval} (default: @code{30}) (type: maybe-integer)
+How often in minutes cached interface data is saved to file when all
+monitored interfaces are offline.  Value range:
+@var{save-interval}..@samp{60}
+
+@item @code{pid-file} (default: @code{"/var/run/vnstatd.pid"}) (type: maybe-string)
+Specify pid file path and name to be used.
+
+@item @code{poll-interval} (default: @code{5}) (type: maybe-integer)
+How often in seconds interfaces are checked for status changes.  Value
+range: @samp{2}..@samp{60}
+
+@item @code{rescan-database-on-save?} (type: maybe-boolean)
+Automatically discover added interfaces from the database and start
+monitoring.  The rescan is done every @var{save-interval} or
+@var{offline-save-interval} minutes depending on the current activity
+state.
+
+@item @code{save-interval} (default: @code{5}) (type: maybe-integer)
+How often in minutes cached interface data is saved to file.  Value
+range: ( @var{update-interval} / 60 )..@samp{60}
+
+@item @code{save-on-status-change?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the additional saving to file of cached interface data
+when the availability of an interface changes, i.e., when an interface
+goes offline or comes online.
+
+@item @code{time-sync-wait} (default: @code{5}) (type: maybe-integer)
+How many minutes to wait during daemon startup for system clock to sync
+if most recent database update appears to be in the future.  This may be
+needed in systems without a real-time clock (RTC) which require some
+time after boot to query and set the correct time.  @code{0} = wait
+disabled.  Value range: @samp{0}..@samp{60}
+
+@item @code{top-day-entries} (default: @code{20}) (type: maybe-integer)
+Data retention duration for the top day entries.  The configuration
+defines how many of the past top day entries will be stored.  Set to
+@code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{trafficless-entries?} (default: @code{#t}) (type: maybe-boolean)
+Create database entries even when there is no traffic during the entry's
+time period.
+
+@item @code{update-file-owner?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the update of file ownership during daemon process
+startup.  During daemon startup, only database, log and pid files will
+be modified if the user or group change feature ( @var{daemon-user} or
+@var{daemon-group} ) is enabled and the files don't match the requested
+user or group.  During manual database creation, this option will cause
+file ownership to be inherited from the database directory if the
+directory already exists.  This option only has effect when the process
+is started as root or via sudo.
+
+@item @code{update-interval} (default: @code{20}) (type: maybe-integer)
+How often in seconds the interface data is updated.  Value range:
+@var{poll-interval}..@samp{300}
+
+@item @code{use-logging} (default: @code{2}) (type: maybe-integer)
+Enable or disable logging.  This option is ignored when the daemon is
+started with .B "-n, --nodaemon" which results in all log output being
+shown in terminal the daemon process is using.  @code{0} = disabled,
+@code{1} = logfile and @code{2} = syslog.
+
+@item @code{use-UTC?} (type: maybe-boolean)
+Enable or disable using UTC as timezone in the database for all entries.
+When enabled, all entries added to the database will use UTC regardless
+of the configured system timezone.  When disabled, the configured system
+timezone will be used.  Changing this setting will not result in already
+existing data to be modified.
+
+@item @code{yearly-years} (default: @code{-1}) (type: maybe-integer)
+Data retention duration for the one year resolution entries.  The
+configuration defines for how many past years entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@end table
+@end deftp
+@c %end of fragment
+
 @subsubheading Zabbix server
 @cindex zabbix zabbix-server
 Zabbix is a high performance monitoring system that can collect data from a
diff --git a/gnu/services/monitoring.scm b/gnu/services/monitoring.scm
index 44e2e8886c..870943e80b 100644
--- a/gnu/services/monitoring.scm
+++ b/gnu/services/monitoring.scm
@@ -3,6 +3,7 @@
 ;;; Copyright © 2018, 2019 Gábor Boskovits <boskovits@gmail.com>
 ;;; Copyright © 2018, 2019, 2020 Oleg Pykhalov <go.wigust@gmail.com>
 ;;; Copyright © 2022 Marius Bakke <marius@gnu.org>
+;;; Copyright © 2023 Bruno Victal <mirai@makinata.eu>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -26,6 +27,7 @@ (define-module (gnu services monitoring)
   #:use-module (gnu services web)
   #:use-module (gnu packages admin)
   #:use-module (gnu packages monitoring)
+  #:use-module (gnu packages networking)
   #:use-module (gnu system shadow)
   #:use-module (guix gexp)
   #:use-module (guix packages)
@@ -34,6 +36,7 @@ (define-module (gnu services monitoring)
   #:use-module ((guix ui) #:select (display-hint G_))
   #:use-module (ice-9 match)
   #:use-module (ice-9 rdelim)
+  #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-26)
   #:use-module (srfi srfi-35)
   #:export (darkstat-configuration
@@ -45,6 +48,46 @@ (define-module (gnu services monitoring)
             prometheus-node-exporter-web-listen-address
             prometheus-node-exporter-service-type
 
+            vnstat-configuration
+            vnstat-configuration?
+            vnstat-service-type
+            vnstat-configuration-package
+            vnstat-configuration-database-dir
+            vnstat-configuration-5-minute-hours
+            vnstat-configuration-64bit-interface-counters
+            vnstat-configuration-always-add-new-interfaces?
+            vnstat-configuration-bandwidth-detection?
+            vnstat-configuration-bandwidth-detection-interval
+            vnstat-configuration-boot-variation
+            vnstat-configuration-check-disk-space?
+            vnstat-configuration-create-dirs?
+            vnstat-configuration-daemon-group
+            vnstat-configuration-daemon-user
+            vnstat-configuration-daily-days
+            vnstat-configuration-database-synchronous
+            vnstat-configuration-database-write-ahead-logging?
+            vnstat-configuration-hourly-days
+            vnstat-configuration-log-file
+            vnstat-configuration-max-bandwidth
+            vnstat-configuration-max-BW
+            vnstat-configuration-monthly-months
+            vnstat-configuration-month-rotate
+            vnstat-configuration-month-rotate-affects-years?
+            vnstat-configuration-offline-save-interval
+            vnstat-configuration-pid-file
+            vnstat-configuration-poll-interval
+            vnstat-configuration-rescan-database-on-save?
+            vnstat-configuration-save-interval
+            vnstat-configuration-save-on-status-change?
+            vnstat-configuration-time-sync-wait
+            vnstat-configuration-top-day-entries
+            vnstat-configuration-trafficless-entries?
+            vnstat-configuration-update-file-owner?
+            vnstat-configuration-update-interval
+            vnstat-configuration-use-logging
+            vnstat-configuration-use-UTC?
+            vnstat-configuration-yearly-years
+
             zabbix-server-configuration
             zabbix-server-service-type
             zabbix-agent-configuration
@@ -196,6 +239,414 @@ (define prometheus-node-exporter-service-type
                         prometheus-node-exporter-shepherd-service)))
    (default-value (prometheus-node-exporter-configuration))))
 
+\f
+;;;
+;;; vnstat daemon
+;;;
+
+(define* (camelfy-field-name field-name #:key (dromedary? #f))
+  (match (string-split (symbol->string field-name) #\-)
+    ((head tail ...)
+     (string-join (cons (if dromedary? head (string-upcase head 0 1))
+                        (map (cut string-upcase <> 0 1) tail)) ""))))
+
+(define (strip-trailing-?-character field-name)
+  "Drop rightmost '?' character"
+  (let ((str (symbol->string field-name)))
+    (if (string-suffix? "?" str)
+        (string->symbol (string-drop-right str 1))
+        field-name)))
+
+(define (vnstat-serialize-string field-name value)
+  #~(format #f "~a ~s~%"
+            #$(camelfy-field-name field-name)
+            #$value))
+
+(define vnstat-serialize-integer vnstat-serialize-string)
+
+(define (vnstat-serialize-boolean field-name value)
+  #~(format #f "~a ~a~%"
+            #$(camelfy-field-name (strip-trailing-?-character field-name))
+            #$(if value 1 0)))
+
+(define (vnstat-serialize-alist field-name value)
+  (generic-serialize-alist string-append
+                           (lambda (iface val)
+                             (vnstat-serialize-integer
+                              (format #f "MaxBW~a" iface) val))
+                           value))
+
+(define-maybe string  (prefix vnstat-))
+(define-maybe integer (prefix vnstat-))
+(define-maybe boolean (prefix vnstat-))
+(define-maybe alist   (prefix vnstat-))
+
+;; Documentation strings from vnstat.conf manpage adapted to texinfo.
+;; vnstat checkout: v2.10, commit b3408af1c609aa6265d296cab7bfe59a61d7cf70
+;; Do not reflow these strings or drop the initial \ escape as it makes it
+;; harder to diff against the manpage.
+(define-configuration vnstat-configuration
+  (package
+    (file-like vnstat)
+    "The vnstat package."
+    empty-serializer)
+
+  (database-dir
+   (string "/var/lib/vnstat")
+   "\
+Specifies the directory where the database is to be stored.
+A full path must be given and a leading '/' isn't required.")  
+
+  (5-minute-hours
+   (maybe-integer 48)
+   "\
+Data retention duration for the 5 minute resolution entries. The configuration
+defines for how many past hours entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (64bit-interface-counters
+   (maybe-integer -2)
+   "\
+Select interface counter handling. Set to @code{1} for defining that all interfaces
+use 64-bit counters on the kernel side and @code{0} for defining 32-bit counter. Set
+to @code{-1} for using the old style logic used in earlier versions where counter
+values within 32-bits are assumed to be 32-bit and anything larger is assumed to
+be a 64-bit counter. This may produce false results if a 64-bit counter is
+reset within the 32-bits. Set to @code{-2} for using automatic detection based on
+available kernel datastructures.")
+
+  (always-add-new-interfaces?
+   (maybe-boolean #t)
+   "\
+Enable or disable automatic creation of new database entries for interfaces not
+currently in the database even if the database file already exists when the
+daemon is started. New database entries will also get created for new interfaces
+seen while the daemon is running. Pseudo interfaces @samp{lo}, @samp{lo0} and @samp{sit0} are always
+excluded from getting added.")
+
+  (bandwidth-detection?
+   (maybe-boolean #t)
+   "\
+Try to automatically detect
+@var{max-bandwidth}
+value for each monitored interface. Mostly only ethernet interfaces support
+this feature.
+@var{max-bandwidth}
+will be used as fallback value if detection fails. Any interface specific
+@var{max-BW}
+configuration will disable the detection for the specified interface.
+In Linux, the detection is disabled for tun interfaces due to the
+Linux kernel always reporting 10 Mbit regardless of the used real interface.")
+
+  (bandwidth-detection-interval
+   (maybe-integer 5)
+   "\
+How often in minutes interface specific detection of
+@var{max-bandwidth}
+is done for detecting possible changes when
+@var{bandwidth-detection}
+is enabled. Can be disabled by setting to @code{0}. Value range: @samp{0}..@samp{30}")
+
+  (boot-variation
+   (maybe-integer 15)
+   "\
+Time in seconds how much the boot time reported by system kernel can variate
+between updates. Value range: @samp{0}..@samp{300}")
+
+  (check-disk-space?
+   (maybe-boolean #t)
+   "\
+Enable or disable the availability check of at least some free disk space before
+a database write.")
+
+  (create-dirs?
+   (maybe-boolean #t)
+   "\
+Enable or disable the creation of directories when a configured path doesn't
+exist. This includes @var{database-dir}.")
+
+  (daemon-group
+   maybe-string
+   "\
+Specify the group to which the daemon process should switch during startup.
+The group can either be the name of the group or a numerical group id.
+Leave empty to disable group switching. This option can only be used when
+the process is started as root.")
+
+  (daemon-user
+   maybe-string
+   "\
+Specify the user to which the daemon process should switch during startup.
+The user can either be the login of the user or a numerical user id.
+Leave empty to disable user switching. This option can only be used when
+the process is started as root.")
+
+  (daily-days
+   (maybe-integer 62)
+   "\
+Data retention duration for the one day resolution entries. The configuration
+defines for how many past days entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (database-synchronous
+   (maybe-integer -1)
+   "\
+Change the setting of the SQLite \"synchronous\" flag which controls how much
+care is taken to ensure disk writes have fully completed when writing data to
+the database before continuing other actions. Higher values take extra steps
+to ensure data safety at the cost of slower performance. A value of @code{0} will
+result in all handling being left to the filesystem itself. Set to @code{-1} to
+select the default value according to database mode controlled by
+@var{database-write-ahead-logging}
+setting. See SQLite documentation for more details regarding values from @code{1}
+to @code{3}. Value range: @samp{-1}..@samp{3}")
+
+  (database-write-ahead-logging?
+   (maybe-boolean #f)
+   "\
+Enable or disable SQLite Write-Ahead Logging mode for the database. See SQLite
+documentation for more details and note that support for read-only operations
+isn't available in older SQLite versions.")
+
+  (hourly-days
+   (maybe-integer 4)
+   "\
+Data retention duration for the one hour resolution entries. The configuration
+defines for how many past days entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (log-file
+   maybe-string
+   "\
+Specify log file path and name to be used if @var{use-logging} is set to @code{1}.")
+
+  (max-bandwidth
+   maybe-integer
+   "\
+Maximum bandwidth for all interfaces. If the interface specific traffic
+exceeds the given value then the data is assumed to be invalid and rejected.
+Set to 0 in order to disable the feature. Value range: @samp{0}..@samp{50000}")
+
+  ;; documentation adapted for alist type
+  (max-BW
+   maybe-alist
+   "\
+Same as
+@var{max-bandwidth}
+but can be used for setting individual limits
+for selected interfaces. This is an association list of interfaces
+as symbols/strings to integer values. For example,
+@lisp
+(max-BW
+ `((eth0 . 15000)
+   (ppp0 . 10000)))
+@end lisp
+@var{bandwidth-detection}
+is disabled on an interface specific level for each
+@var{max-BW}
+configuration. Value range: @samp{0}..@samp{50000}")
+
+  (monthly-months
+   (maybe-integer 25)
+   "\
+Data retention duration for the one month resolution entries. The configuration
+defines for how many past months entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (month-rotate
+   (maybe-integer 1)
+   "\
+Day of month that months are expected to change. Usually set to
+1 but can be set to alternative values for example for tracking
+monthly billed traffic where the billing period doesn't start on
+the first day. For example, if set to 7, days of February up to and
+including the 6th will count for January. Changing this option will
+not cause existing data to be recalculated. Value range: @samp{1}..@samp{28}")
+
+  (month-rotate-affects-years?
+   (maybe-boolean #f)
+   "\
+Enable or disable
+@var{month-rotate}
+also affecting yearly data. Applicable only when
+@var{month-rotate}
+has a value greater than one.")
+
+  (offline-save-interval
+   (maybe-integer 30)
+   "\
+How often in minutes cached interface data is saved to file when all monitored
+interfaces are offline. Value range:
+@var{save-interval}..@samp{60}")
+
+  (pid-file
+   (maybe-string "/var/run/vnstatd.pid")
+   "\
+Specify pid file path and name to be used.")
+
+  (poll-interval
+   (maybe-integer 5)
+   "\
+How often in seconds interfaces are checked for status changes.
+Value range: @samp{2}..@samp{60}")
+
+  (rescan-database-on-save?
+   maybe-boolean
+   "\
+Automatically discover added interfaces from the database and start monitoring.
+The rescan is done every
+@var{save-interval}
+or
+@var{offline-save-interval}
+minutes depending on the current activity state.")
+
+  (save-interval
+   (maybe-integer 5)
+   "\
+How often in minutes cached interface data is saved to file.
+Value range: (
+@var{update-interval} / 60 )..@samp{60}")
+
+  (save-on-status-change?
+   (maybe-boolean #t)
+   "\
+Enable or disable the additional saving to file of cached interface data
+when the availability of an interface changes, i.e., when an interface goes
+offline or comes online.")
+
+  (time-sync-wait
+   (maybe-integer 5)
+   "\
+How many minutes to wait during daemon startup for system clock to sync if
+most recent database update appears to be in the future. This may be needed
+in systems without a real-time clock (RTC) which require some time after boot
+to query and set the correct time. @code{0} = wait disabled.
+Value range: @samp{0}..@samp{60}")
+
+  (top-day-entries
+   (maybe-integer 20)
+   "\
+Data retention duration for the top day entries. The configuration
+defines how many of the past top day entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (trafficless-entries?
+   (maybe-boolean #t)
+   "\
+Create database entries even when there is no traffic during the entry's time
+period.")
+
+  (update-file-owner?
+   (maybe-boolean #t)
+   "\
+Enable or disable the update of file ownership during daemon process startup.
+During daemon startup, only database, log and pid files will be modified if the
+user or group change feature (
+@var{daemon-user}
+or
+@var{daemon-group}
+) is enabled and the files don't match the requested user or group. During manual
+database creation, this option will cause file ownership to be inherited from the
+database directory if the directory already exists. This option only has effect
+when the process is started as root or via sudo.")
+
+  (update-interval
+   (maybe-integer 20)
+   "\
+How often in seconds the interface data is updated. Value range:
+@var{poll-interval}..@samp{300}")
+
+  (use-logging
+   (maybe-integer 2)
+   "\
+Enable or disable logging. This option is ignored when the daemon is started with
+.B \"-n, --nodaemon\"
+which results in all log output being shown in terminal the daemon process is using.
+@code{0} = disabled, @code{1} = logfile and @code{2} = syslog.")
+
+  (use-UTC?
+   maybe-boolean
+   "\
+Enable or disable using UTC as timezone in the database for all entries. When
+enabled, all entries added to the database will use UTC regardless of the
+configured system timezone. When disabled, the configured system timezone
+will be used. Changing this setting will not result in already existing
+data to be modified.")
+
+  (yearly-years
+   (maybe-integer -1)
+   "\
+Data retention duration for the one year resolution entries. The configuration
+defines for how many past years entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (prefix vnstat-))
+
+(define (vnstat-serialize-configuration config)
+  (mixed-text-file
+   "vnstat.conf"
+   (serialize-configuration config vnstat-configuration-fields)))
+
+(define (vnstat-shepherd-service config)
+  (let ((config-file (vnstat-serialize-configuration config)))
+    (match-record config <vnstat-configuration> (package pid-file)
+      (shepherd-service
+       (documentation "Run vnstatd.")
+       (requirement `(networking))
+       (provision '(vnstatd))
+       (start #~(make-forkexec-constructor
+                 (list #$(file-append package "/sbin/vnstatd")
+                       "--daemon"
+                       "--config" #$config-file)
+                 #:pid-file #$pid-file))
+       (stop #~(make-kill-destructor))
+       (actions
+        (list (shepherd-configuration-action config-file)
+              (shepherd-action
+               (name 'reload)
+               (documentation "Reload vnstatd.")
+               (procedure
+                #~(lambda (pid)
+                    (if pid
+                        (begin
+                          (kill pid SIGHUP)
+                          (format #t
+                                  "Issued SIGHUP to vnstatd (PID ~a)."
+                                  pid))
+                        (format #t "vnstatd is not running.")))))))))))
+
+(define (vnstat-account-service config)
+  (match-record config <vnstat-configuration> (daemon-group daemon-user)
+    (if (every maybe-value-set? (list daemon-group daemon-user))
+        (list
+         (user-group
+          (name daemon-group)
+          (system? #t))
+         (user-account
+          (name daemon-user)
+          (group daemon-group)
+          (system? #t)
+          (home-directory "/var/empty")
+          (shell (file-append shadow "/sbin/nologin"))))
+        '())))
+
+(define vnstat-service-type
+  (service-type
+   (name 'vnstat)
+   (description "vnStat network-traffic monitor service.")
+   (extensions
+    (list (service-extension shepherd-root-service-type
+                             (compose list vnstat-shepherd-service))
+          (service-extension account-service-type
+                             vnstat-account-service)))
+   (default-value (vnstat-configuration))))
+
 \f
 ;;;
 ;;; Zabbix server
-- 
2.38.1





^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH] services: Add vnstat-service-type.
  2023-02-07 14:25 ` [bug#60788] [PATCH v4] " Bruno Victal
@ 2023-02-09  3:34   ` Maxim Cournoyer
  2023-02-09  4:19     ` Bruno Victal
  0 siblings, 1 reply; 27+ messages in thread
From: Maxim Cournoyer @ 2023-02-09  3:34 UTC (permalink / raw)
  To: Bruno Victal; +Cc: 60788

Hi Bruno,

Bruno Victal <mirai@makinata.eu> writes:

> * gnu/services/monitoring.scm (vnstat-service-type): New variable.
> * doc/guix.texi (Monitoring Services): Document it.
> ---
>  doc/guix.texi               | 240 ++++++++++++++++++-
>  gnu/services/monitoring.scm | 451 ++++++++++++++++++++++++++++++++++++
>  2 files changed, 689 insertions(+), 2 deletions(-)

Two remaining comments from me:

1. I'd normalize all the configuration field names to lowercase, so
max-BW and use-UTC? would become max-bw and use-utc?, for example.

2. I'd add a simple example vnstat usage example to the manual.

With this done, I think it should be good to go!

-- 
Thanks,
Maxim




^ permalink raw reply	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH] services: Add vnstat-service-type.
  2023-02-09  3:34   ` [bug#60788] [PATCH] " Maxim Cournoyer
@ 2023-02-09  4:19     ` Bruno Victal
  2023-02-09 13:31       ` Maxim Cournoyer
  0 siblings, 1 reply; 27+ messages in thread
From: Bruno Victal @ 2023-02-09  4:19 UTC (permalink / raw)
  To: Maxim Cournoyer; +Cc: 60788

Hi Maxim,

On 2023-02-09 03:34, Maxim Cournoyer wrote:
> Two remaining comments from me:
> 
> 1. I'd normalize all the configuration field names to lowercase, so
> max-BW and use-UTC? would become max-bw and use-utc?, for example.

No, this is intentional so that uglify+case-conversion yields correct configuration directives.

> 2. I'd add a simple example vnstat usage example to the manual.

The service is "ready to be used" with the default values, so I didn't think
it'd add much value adding an example here. Do you think we should add one?


Cheers,
Bruno




^ permalink raw reply	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH] services: Add vnstat-service-type.
  2023-02-09  4:19     ` Bruno Victal
@ 2023-02-09 13:31       ` Maxim Cournoyer
  0 siblings, 0 replies; 27+ messages in thread
From: Maxim Cournoyer @ 2023-02-09 13:31 UTC (permalink / raw)
  To: Bruno Victal; +Cc: 60788

Hi Bruno,

Bruno Victal <mirai@makinata.eu> writes:

> Hi Maxim,
>
> On 2023-02-09 03:34, Maxim Cournoyer wrote:
>> Two remaining comments from me:
>>
>> 1. I'd normalize all the configuration field names to lowercase, so
>> max-BW and use-UTC? would become max-bw and use-utc?, for example.
>
> No, this is intentional so that uglify+case-conversion yields correct configuration directives.

OK; the case conversion could be special-cased (max-bw -> max-BW,
use-utc? -> utc-UTC?) in the uglify+case-conversion logic.  But it seems
that vnstat doesn't care about case when parsing its options; it uses
strcasecmp, which ignores the case of the characters per 'man 3
strcasecmp' [0].

[0]  https://github.com/vergoh/vnstat/blob/master/src/cfg.c#L149

>> 2. I'd add a simple example vnstat usage example to the manual.
>
> The service is "ready to be used" with the default values, so I didn't think
> it'd add much value adding an example here. Do you think we should add one?

I think so; with so many switches it can appear intimidating; but a
minimal working example would show to users that they can start simple.

-- 
Thanks,
Maxim




^ permalink raw reply	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH v5] services: Add vnstat-service-type.
  2023-01-13 20:07 [bug#60788] [PATCH] services: Add vnstat-service-type Bruno Victal
                   ` (5 preceding siblings ...)
  2023-02-07 14:25 ` [bug#60788] [PATCH v4] " Bruno Victal
@ 2023-02-10 13:15 ` Bruno Victal
  2023-02-10 14:07   ` Maxim Cournoyer
  2023-02-10 14:14 ` [bug#60788] [PATCH v6] " Bruno Victal
                   ` (6 subsequent siblings)
  13 siblings, 1 reply; 27+ messages in thread
From: Bruno Victal @ 2023-02-10 13:15 UTC (permalink / raw)
  To: 60788; +Cc: Bruno Victal, maxim.cournoyer

* gnu/services/monitoring.scm (vnstat-service-type): New variable.
* doc/guix.texi (Monitoring Services): Document it.
---
 doc/guix.texi               | 246 ++++++++++++++++++-
 gnu/services/monitoring.scm | 457 ++++++++++++++++++++++++++++++++++++
 2 files changed, 701 insertions(+), 2 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index 5fb5850a6c..c54e9e5beb 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -109,11 +109,10 @@
 Copyright @copyright{} 2022 Simon Streit@*
 Copyright @copyright{} 2022 (@*
 Copyright @copyright{} 2022 John Kehayias@*
-Copyright @copyright{} 2022 Bruno Victal@*
+Copyright @copyright{} 2022⁠–⁠2023 Bruno Victal@*
 Copyright @copyright{} 2022 Ivan Vilata-i-Balaguer@*
 Copyright @copyright{} 2023 Giacomo Leidi@*
 Copyright @copyright{} 2022 Antero Mejr@*
-Copyright @copyright{} 2023 Bruno Victal@*
 
 Permission is granted to copy, distribute and/or modify this document
 under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -28241,6 +28240,249 @@ Monitoring Services
 @end table
 @end deftp
 
+@anchor{vnstat}
+@subsubheading vnStat Network Traffic Monitor
+@cindex vnstat
+
+vnStat is a network traffic monitor that uses interface statistics provided
+by the kernel rather than traffic sniffing.  This makes it a light resource
+monitor, regardless of network traffic rate.
+
+@defvar vnstat-service-type
+This is the service type for the @uref{https://humdi.net/vnstat/,vnStat} daemon
+and accepts a @code{vnstat-configuration} value.
+
+The following example will configure the service with default values:
+
+@lisp
+(service vnstat-service-type)
+@end lisp
+@end defvar
+
+@c %start of fragment
+@deftp {Data Type} vnstat-configuration
+Available @code{vnstat-configuration} fields are:
+
+@table @asis
+@item @code{package} (default: @code{vnstat}) (type: file-like)
+The vnstat package.
+
+@item @code{database-dir} (default: @code{"/var/lib/vnstat"}) (type: string)
+Specifies the directory where the database is to be stored.  A full path
+must be given and a leading '/' isn't required.
+
+@item @code{5-minute-hours} (default: @code{48}) (type: maybe-integer)
+Data retention duration for the 5 minute resolution entries.  The
+configuration defines for how many past hours entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@item @code{64bit-interface-counters} (default: @code{-2}) (type: maybe-integer)
+Select interface counter handling.  Set to @code{1} for defining that
+all interfaces use 64-bit counters on the kernel side and @code{0} for
+defining 32-bit counter.  Set to @code{-1} for using the old style logic
+used in earlier versions where counter values within 32-bits are assumed
+to be 32-bit and anything larger is assumed to be a 64-bit counter.  This
+may produce false results if a 64-bit counter is reset within the
+32-bits.  Set to @code{-2} for using automatic detection based on
+available kernel datastructures.
+
+@item @code{always-add-new-interfaces?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable automatic creation of new database entries for
+interfaces not currently in the database even if the database file
+already exists when the daemon is started.  New database entries will
+also get created for new interfaces seen while the daemon is running.
+Pseudo interfaces @samp{lo}, @samp{lo0} and @samp{sit0} are always excluded from getting
+added.
+
+@item @code{bandwidth-detection?} (default: @code{#t}) (type: maybe-boolean)
+Try to automatically detect @var{max-bandwidth} value for each monitored
+interface.  Mostly only ethernet interfaces support this feature.
+@var{max-bandwidth} will be used as fallback value if detection fails.
+Any interface specific @var{max-BW} configuration will disable the
+detection for the specified interface.  In Linux, the detection is
+disabled for tun interfaces due to the Linux kernel always reporting 10
+Mbit regardless of the used real interface.
+
+@item @code{bandwidth-detection-interval} (default: @code{5}) (type: maybe-integer)
+How often in minutes interface specific detection of @var{max-bandwidth}
+is done for detecting possible changes when @var{bandwidth-detection} is
+enabled.  Can be disabled by setting to @code{0}.  Value range:
+@samp{0}..@samp{30}
+
+@item @code{boot-variation} (default: @code{15}) (type: maybe-integer)
+Time in seconds how much the boot time reported by system kernel can
+variate between updates.  Value range: @samp{0}..@samp{300}
+
+@item @code{check-disk-space?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the availability check of at least some free disk
+space before a database write.
+
+@item @code{create-dirs?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the creation of directories when a configured path
+doesn't exist.  This includes @var{database-dir}.
+
+@item @code{daemon-group} (type: maybe-string)
+Specify the group to which the daemon process should switch during
+startup.  The group can either be the name of the group or a numerical
+group id.  Leave empty to disable group switching.  This option can only
+be used when the process is started as root.
+
+@item @code{daemon-user} (type: maybe-string)
+Specify the user to which the daemon process should switch during
+startup.  The user can either be the login of the user or a numerical
+user id.  Leave empty to disable user switching.  This option can only
+be used when the process is started as root.
+
+@item @code{daily-days} (default: @code{62}) (type: maybe-integer)
+Data retention duration for the one day resolution entries.  The
+configuration defines for how many past days entries will be stored.  Set
+to @code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{database-synchronous} (default: @code{-1}) (type: maybe-integer)
+Change the setting of the SQLite "synchronous" flag which controls how
+much care is taken to ensure disk writes have fully completed when
+writing data to the database before continuing other actions.  Higher
+values take extra steps to ensure data safety at the cost of slower
+performance.  A value of @code{0} will result in all handling being left
+to the filesystem itself.  Set to @code{-1} to select the default value
+according to database mode controlled by
+@var{database-write-ahead-logging} setting.  See SQLite documentation
+for more details regarding values from @code{1} to @code{3}.  Value
+range: @samp{-1}..@samp{3}
+
+@item @code{database-write-ahead-logging?} (default: @code{#f}) (type: maybe-boolean)
+Enable or disable SQLite Write-Ahead Logging mode for the database.  See
+SQLite documentation for more details and note that support for
+read-only operations isn't available in older SQLite versions.
+
+@item @code{hourly-days} (default: @code{4}) (type: maybe-integer)
+Data retention duration for the one hour resolution entries.  The
+configuration defines for how many past days entries will be stored.  Set
+to @code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{log-file} (type: maybe-string)
+Specify log file path and name to be used if @var{use-logging} is set to
+@code{1}.
+
+@item @code{max-bandwidth} (type: maybe-integer)
+Maximum bandwidth for all interfaces.  If the interface specific traffic
+exceeds the given value then the data is assumed to be invalid and
+rejected.  Set to 0 in order to disable the feature.  Value range:
+@samp{0}..@samp{50000}
+
+@item @code{max-bw} (type: maybe-alist)
+Same as @var{max-bandwidth} but can be used for setting individual
+limits for selected interfaces.  This is an association list of
+interfaces as symbols/strings to integer values.  For example,
+@lisp
+(max-bw `((eth0 .  15000)
+          (ppp0 .  10000)))
+@end lisp
+@var{bandwidth-detection} is disabled on an interface specific level for
+each @var{max-bw} configuration.  Value range: @samp{0}..@samp{50000}
+
+@item @code{monthly-months} (default: @code{25}) (type: maybe-integer)
+Data retention duration for the one month resolution entries.  The
+configuration defines for how many past months entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@item @code{month-rotate} (default: @code{1}) (type: maybe-integer)
+Day of month that months are expected to change.  Usually set to 1 but
+can be set to alternative values for example for tracking monthly billed
+traffic where the billing period doesn't start on the first day.  For
+example, if set to 7, days of February up to and including the 6th will
+count for January.  Changing this option will not cause existing data to
+be recalculated.  Value range: @samp{1}..@samp{28}
+
+@item @code{month-rotate-affects-years?} (default: @code{#f}) (type: maybe-boolean)
+Enable or disable @var{month-rotate} also affecting yearly data.
+Applicable only when @var{month-rotate} has a value greater than one.
+
+@item @code{offline-save-interval} (default: @code{30}) (type: maybe-integer)
+How often in minutes cached interface data is saved to file when all
+monitored interfaces are offline.  Value range:
+@var{save-interval}..@samp{60}
+
+@item @code{pid-file} (default: @code{"/var/run/vnstatd.pid"}) (type: maybe-string)
+Specify pid file path and name to be used.
+
+@item @code{poll-interval} (default: @code{5}) (type: maybe-integer)
+How often in seconds interfaces are checked for status changes.  Value
+range: @samp{2}..@samp{60}
+
+@item @code{rescan-database-on-save?} (type: maybe-boolean)
+Automatically discover added interfaces from the database and start
+monitoring.  The rescan is done every @var{save-interval} or
+@var{offline-save-interval} minutes depending on the current activity
+state.
+
+@item @code{save-interval} (default: @code{5}) (type: maybe-integer)
+How often in minutes cached interface data is saved to file.  Value
+range: ( @var{update-interval} / 60 )..@samp{60}
+
+@item @code{save-on-status-change?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the additional saving to file of cached interface data
+when the availability of an interface changes, i.e., when an interface
+goes offline or comes online.
+
+@item @code{time-sync-wait} (default: @code{5}) (type: maybe-integer)
+How many minutes to wait during daemon startup for system clock to sync
+if most recent database update appears to be in the future.  This may be
+needed in systems without a real-time clock (RTC) which require some
+time after boot to query and set the correct time.  @code{0} = wait
+disabled.  Value range: @samp{0}..@samp{60}
+
+@item @code{top-day-entries} (default: @code{20}) (type: maybe-integer)
+Data retention duration for the top day entries.  The configuration
+defines how many of the past top day entries will be stored.  Set to
+@code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{trafficless-entries?} (default: @code{#t}) (type: maybe-boolean)
+Create database entries even when there is no traffic during the entry's
+time period.
+
+@item @code{update-file-owner?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the update of file ownership during daemon process
+startup.  During daemon startup, only database, log and pid files will
+be modified if the user or group change feature ( @var{daemon-user} or
+@var{daemon-group} ) is enabled and the files don't match the requested
+user or group.  During manual database creation, this option will cause
+file ownership to be inherited from the database directory if the
+directory already exists.  This option only has effect when the process
+is started as root or via sudo.
+
+@item @code{update-interval} (default: @code{20}) (type: maybe-integer)
+How often in seconds the interface data is updated.  Value range:
+@var{poll-interval}..@samp{300}
+
+@item @code{use-logging} (default: @code{2}) (type: maybe-integer)
+Enable or disable logging.  This option is ignored when the daemon is
+started with .B "-n, --nodaemon" which results in all log output being
+shown in terminal the daemon process is using.  @code{0} = disabled,
+@code{1} = logfile and @code{2} = syslog.
+
+@item @code{use-utc?} (type: maybe-boolean)
+Enable or disable using UTC as timezone in the database for all entries.
+When enabled, all entries added to the database will use UTC regardless
+of the configured system timezone.  When disabled, the configured system
+timezone will be used.  Changing this setting will not result in already
+existing data to be modified.
+
+@item @code{yearly-years} (default: @code{-1}) (type: maybe-integer)
+Data retention duration for the one year resolution entries.  The
+configuration defines for how many past years entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@end table
+@end deftp
+@c %end of fragment
+
 @subsubheading Zabbix server
 @cindex zabbix zabbix-server
 Zabbix is a high performance monitoring system that can collect data from a
diff --git a/gnu/services/monitoring.scm b/gnu/services/monitoring.scm
index 44e2e8886c..e7b515d6b0 100644
--- a/gnu/services/monitoring.scm
+++ b/gnu/services/monitoring.scm
@@ -3,6 +3,7 @@
 ;;; Copyright © 2018, 2019 Gábor Boskovits <boskovits@gmail.com>
 ;;; Copyright © 2018, 2019, 2020 Oleg Pykhalov <go.wigust@gmail.com>
 ;;; Copyright © 2022 Marius Bakke <marius@gnu.org>
+;;; Copyright © 2023 Bruno Victal <mirai@makinata.eu>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -26,6 +27,7 @@ (define-module (gnu services monitoring)
   #:use-module (gnu services web)
   #:use-module (gnu packages admin)
   #:use-module (gnu packages monitoring)
+  #:use-module (gnu packages networking)
   #:use-module (gnu system shadow)
   #:use-module (guix gexp)
   #:use-module (guix packages)
@@ -34,6 +36,7 @@ (define-module (gnu services monitoring)
   #:use-module ((guix ui) #:select (display-hint G_))
   #:use-module (ice-9 match)
   #:use-module (ice-9 rdelim)
+  #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-26)
   #:use-module (srfi srfi-35)
   #:export (darkstat-configuration
@@ -45,6 +48,46 @@ (define-module (gnu services monitoring)
             prometheus-node-exporter-web-listen-address
             prometheus-node-exporter-service-type
 
+            vnstat-configuration
+            vnstat-configuration?
+            vnstat-service-type
+            vnstat-configuration-package
+            vnstat-configuration-database-dir
+            vnstat-configuration-5-minute-hours
+            vnstat-configuration-64bit-interface-counters
+            vnstat-configuration-always-add-new-interfaces?
+            vnstat-configuration-bandwidth-detection?
+            vnstat-configuration-bandwidth-detection-interval
+            vnstat-configuration-boot-variation
+            vnstat-configuration-check-disk-space?
+            vnstat-configuration-create-dirs?
+            vnstat-configuration-daemon-group
+            vnstat-configuration-daemon-user
+            vnstat-configuration-daily-days
+            vnstat-configuration-database-synchronous
+            vnstat-configuration-database-write-ahead-logging?
+            vnstat-configuration-hourly-days
+            vnstat-configuration-log-file
+            vnstat-configuration-max-bandwidth
+            vnstat-configuration-max-BW
+            vnstat-configuration-monthly-months
+            vnstat-configuration-month-rotate
+            vnstat-configuration-month-rotate-affects-years?
+            vnstat-configuration-offline-save-interval
+            vnstat-configuration-pid-file
+            vnstat-configuration-poll-interval
+            vnstat-configuration-rescan-database-on-save?
+            vnstat-configuration-save-interval
+            vnstat-configuration-save-on-status-change?
+            vnstat-configuration-time-sync-wait
+            vnstat-configuration-top-day-entries
+            vnstat-configuration-trafficless-entries?
+            vnstat-configuration-update-file-owner?
+            vnstat-configuration-update-interval
+            vnstat-configuration-use-logging
+            vnstat-configuration-use-UTC?
+            vnstat-configuration-yearly-years
+
             zabbix-server-configuration
             zabbix-server-service-type
             zabbix-agent-configuration
@@ -196,6 +239,420 @@ (define prometheus-node-exporter-service-type
                         prometheus-node-exporter-shepherd-service)))
    (default-value (prometheus-node-exporter-configuration))))
 
+\f
+;;;
+;;; vnstat daemon
+;;;
+
+(define* (camelfy-field-name field-name #:key (dromedary? #f))
+  (match (string-split (symbol->string field-name) #\-)
+    ((head tail ...)
+     (string-join (cons (if dromedary? head (string-upcase head 0 1))
+                        (map (cut string-upcase <> 0 1) tail)) ""))))
+
+(define (strip-trailing-?-character field-name)
+  "Drop rightmost '?' character"
+  (let ((str (symbol->string field-name)))
+    (if (string-suffix? "?" str)
+        (string->symbol (string-drop-right str 1))
+        field-name)))
+
+(define (vnstat-serialize-string field-name value)
+  #~(format #f "~a ~s~%"
+            #$(camelfy-field-name field-name)
+            #$value))
+
+(define vnstat-serialize-integer vnstat-serialize-string)
+
+(define (vnstat-serialize-boolean field-name value)
+  #~(format #f "~a ~a~%"
+            #$(camelfy-field-name (strip-trailing-?-character field-name))
+            #$(if value 1 0)))
+
+(define (vnstat-serialize-alist field-name value)
+  (generic-serialize-alist string-append
+                           (lambda (iface val)
+                             (vnstat-serialize-integer
+                              (format #f "MaxBW~a" iface) val))
+                           value))
+
+(define-maybe string  (prefix vnstat-))
+(define-maybe integer (prefix vnstat-))
+(define-maybe boolean (prefix vnstat-))
+(define-maybe alist   (prefix vnstat-))
+
+;; Documentation strings from vnstat.conf manpage adapted to texinfo.
+;; vnstat checkout: v2.10, commit b3408af1c609aa6265d296cab7bfe59a61d7cf70
+;; Do not reflow these strings or drop the initial \ escape as it makes it
+;; harder to diff against the manpage.
+(define-configuration vnstat-configuration
+  (package
+    (file-like vnstat)
+    "The vnstat package."
+    empty-serializer)
+
+  (database-dir
+   (string "/var/lib/vnstat")
+   "\
+Specifies the directory where the database is to be stored.
+A full path must be given and a leading '/' isn't required.")  
+
+  (5-minute-hours
+   (maybe-integer 48)
+   "\
+Data retention duration for the 5 minute resolution entries. The configuration
+defines for how many past hours entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (64bit-interface-counters
+   (maybe-integer -2)
+   "\
+Select interface counter handling. Set to @code{1} for defining that all interfaces
+use 64-bit counters on the kernel side and @code{0} for defining 32-bit counter. Set
+to @code{-1} for using the old style logic used in earlier versions where counter
+values within 32-bits are assumed to be 32-bit and anything larger is assumed to
+be a 64-bit counter. This may produce false results if a 64-bit counter is
+reset within the 32-bits. Set to @code{-2} for using automatic detection based on
+available kernel datastructures.")
+
+  (always-add-new-interfaces?
+   (maybe-boolean #t)
+   "\
+Enable or disable automatic creation of new database entries for interfaces not
+currently in the database even if the database file already exists when the
+daemon is started. New database entries will also get created for new interfaces
+seen while the daemon is running. Pseudo interfaces @samp{lo}, @samp{lo0} and @samp{sit0} are always
+excluded from getting added.")
+
+  (bandwidth-detection?
+   (maybe-boolean #t)
+   "\
+Try to automatically detect
+@var{max-bandwidth}
+value for each monitored interface. Mostly only ethernet interfaces support
+this feature.
+@var{max-bandwidth}
+will be used as fallback value if detection fails. Any interface specific
+@var{max-BW}
+configuration will disable the detection for the specified interface.
+In Linux, the detection is disabled for tun interfaces due to the
+Linux kernel always reporting 10 Mbit regardless of the used real interface.")
+
+  (bandwidth-detection-interval
+   (maybe-integer 5)
+   "\
+How often in minutes interface specific detection of
+@var{max-bandwidth}
+is done for detecting possible changes when
+@var{bandwidth-detection}
+is enabled. Can be disabled by setting to @code{0}. Value range: @samp{0}..@samp{30}")
+
+  (boot-variation
+   (maybe-integer 15)
+   "\
+Time in seconds how much the boot time reported by system kernel can variate
+between updates. Value range: @samp{0}..@samp{300}")
+
+  (check-disk-space?
+   (maybe-boolean #t)
+   "\
+Enable or disable the availability check of at least some free disk space before
+a database write.")
+
+  (create-dirs?
+   (maybe-boolean #t)
+   "\
+Enable or disable the creation of directories when a configured path doesn't
+exist. This includes @var{database-dir}.")
+
+  (daemon-group
+   maybe-string
+   "\
+Specify the group to which the daemon process should switch during startup.
+The group can either be the name of the group or a numerical group id.
+Leave empty to disable group switching. This option can only be used when
+the process is started as root.")
+
+  (daemon-user
+   maybe-string
+   "\
+Specify the user to which the daemon process should switch during startup.
+The user can either be the login of the user or a numerical user id.
+Leave empty to disable user switching. This option can only be used when
+the process is started as root.")
+
+  (daily-days
+   (maybe-integer 62)
+   "\
+Data retention duration for the one day resolution entries. The configuration
+defines for how many past days entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (database-synchronous
+   (maybe-integer -1)
+   "\
+Change the setting of the SQLite \"synchronous\" flag which controls how much
+care is taken to ensure disk writes have fully completed when writing data to
+the database before continuing other actions. Higher values take extra steps
+to ensure data safety at the cost of slower performance. A value of @code{0} will
+result in all handling being left to the filesystem itself. Set to @code{-1} to
+select the default value according to database mode controlled by
+@var{database-write-ahead-logging}
+setting. See SQLite documentation for more details regarding values from @code{1}
+to @code{3}. Value range: @samp{-1}..@samp{3}")
+
+  (database-write-ahead-logging?
+   (maybe-boolean #f)
+   "\
+Enable or disable SQLite Write-Ahead Logging mode for the database. See SQLite
+documentation for more details and note that support for read-only operations
+isn't available in older SQLite versions.")
+
+  (hourly-days
+   (maybe-integer 4)
+   "\
+Data retention duration for the one hour resolution entries. The configuration
+defines for how many past days entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (log-file
+   maybe-string
+   "\
+Specify log file path and name to be used if @var{use-logging} is set to @code{1}.")
+
+  (max-bandwidth
+   maybe-integer
+   "\
+Maximum bandwidth for all interfaces. If the interface specific traffic
+exceeds the given value then the data is assumed to be invalid and rejected.
+Set to 0 in order to disable the feature. Value range: @samp{0}..@samp{50000}")
+
+  ;; documentation adapted for alist type
+  (max-bw
+   maybe-alist
+   "\
+Same as
+@var{max-bandwidth}
+but can be used for setting individual limits
+for selected interfaces. This is an association list of interfaces
+as symbols/strings to integer values. For example,
+@lisp
+(max-bw
+ `((eth0 . 15000)
+   (ppp0 . 10000)))
+@end lisp
+@var{bandwidth-detection}
+is disabled on an interface specific level for each
+@var{max-bw}
+configuration. Value range: @samp{0}..@samp{50000}"
+   (lambda (field-name value)
+     (if (maybe-value-set? value)
+         (vnstat-serialize-alist field-name value) "")))
+
+  (monthly-months
+   (maybe-integer 25)
+   "\
+Data retention duration for the one month resolution entries. The configuration
+defines for how many past months entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (month-rotate
+   (maybe-integer 1)
+   "\
+Day of month that months are expected to change. Usually set to
+1 but can be set to alternative values for example for tracking
+monthly billed traffic where the billing period doesn't start on
+the first day. For example, if set to 7, days of February up to and
+including the 6th will count for January. Changing this option will
+not cause existing data to be recalculated. Value range: @samp{1}..@samp{28}")
+
+  (month-rotate-affects-years?
+   (maybe-boolean #f)
+   "\
+Enable or disable
+@var{month-rotate}
+also affecting yearly data. Applicable only when
+@var{month-rotate}
+has a value greater than one.")
+
+  (offline-save-interval
+   (maybe-integer 30)
+   "\
+How often in minutes cached interface data is saved to file when all monitored
+interfaces are offline. Value range:
+@var{save-interval}..@samp{60}")
+
+  (pid-file
+   (maybe-string "/var/run/vnstatd.pid")
+   "\
+Specify pid file path and name to be used.")
+
+  (poll-interval
+   (maybe-integer 5)
+   "\
+How often in seconds interfaces are checked for status changes.
+Value range: @samp{2}..@samp{60}")
+
+  (rescan-database-on-save?
+   maybe-boolean
+   "\
+Automatically discover added interfaces from the database and start monitoring.
+The rescan is done every
+@var{save-interval}
+or
+@var{offline-save-interval}
+minutes depending on the current activity state.")
+
+  (save-interval
+   (maybe-integer 5)
+   "\
+How often in minutes cached interface data is saved to file.
+Value range: (
+@var{update-interval} / 60 )..@samp{60}")
+
+  (save-on-status-change?
+   (maybe-boolean #t)
+   "\
+Enable or disable the additional saving to file of cached interface data
+when the availability of an interface changes, i.e., when an interface goes
+offline or comes online.")
+
+  (time-sync-wait
+   (maybe-integer 5)
+   "\
+How many minutes to wait during daemon startup for system clock to sync if
+most recent database update appears to be in the future. This may be needed
+in systems without a real-time clock (RTC) which require some time after boot
+to query and set the correct time. @code{0} = wait disabled.
+Value range: @samp{0}..@samp{60}")
+
+  (top-day-entries
+   (maybe-integer 20)
+   "\
+Data retention duration for the top day entries. The configuration
+defines how many of the past top day entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (trafficless-entries?
+   (maybe-boolean #t)
+   "\
+Create database entries even when there is no traffic during the entry's time
+period.")
+
+  (update-file-owner?
+   (maybe-boolean #t)
+   "\
+Enable or disable the update of file ownership during daemon process startup.
+During daemon startup, only database, log and pid files will be modified if the
+user or group change feature (
+@var{daemon-user}
+or
+@var{daemon-group}
+) is enabled and the files don't match the requested user or group. During manual
+database creation, this option will cause file ownership to be inherited from the
+database directory if the directory already exists. This option only has effect
+when the process is started as root or via sudo.")
+
+  (update-interval
+   (maybe-integer 20)
+   "\
+How often in seconds the interface data is updated. Value range:
+@var{poll-interval}..@samp{300}")
+
+  (use-logging
+   (maybe-integer 2)
+   "\
+Enable or disable logging. This option is ignored when the daemon is started with
+.B \"-n, --nodaemon\"
+which results in all log output being shown in terminal the daemon process is using.
+@code{0} = disabled, @code{1} = logfile and @code{2} = syslog.")
+
+  (use-utc?
+   maybe-boolean
+   "\
+Enable or disable using UTC as timezone in the database for all entries. When
+enabled, all entries added to the database will use UTC regardless of the
+configured system timezone. When disabled, the configured system timezone
+will be used. Changing this setting will not result in already existing
+data to be modified."
+   (lambda (_ value)
+     (if (maybe-value-set? value)
+         (vnstat-serialize-boolean 'use-UTC value) "")))
+
+  (yearly-years
+   (maybe-integer -1)
+   "\
+Data retention duration for the one year resolution entries. The configuration
+defines for how many past years entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (prefix vnstat-))
+
+(define (vnstat-serialize-configuration config)
+  (mixed-text-file
+   "vnstat.conf"
+   (serialize-configuration config vnstat-configuration-fields)))
+
+(define (vnstat-shepherd-service config)
+  (let ((config-file (vnstat-serialize-configuration config)))
+    (match-record config <vnstat-configuration> (package pid-file)
+      (shepherd-service
+       (documentation "Run vnstatd.")
+       (requirement `(networking))
+       (provision '(vnstatd))
+       (start #~(make-forkexec-constructor
+                 (list #$(file-append package "/sbin/vnstatd")
+                       "--daemon"
+                       "--config" #$config-file)
+                 #:pid-file #$pid-file))
+       (stop #~(make-kill-destructor))
+       (actions
+        (list (shepherd-configuration-action config-file)
+              (shepherd-action
+               (name 'reload)
+               (documentation "Reload vnstatd.")
+               (procedure
+                #~(lambda (pid)
+                    (if pid
+                        (begin
+                          (kill pid SIGHUP)
+                          (format #t
+                                  "Issued SIGHUP to vnstatd (PID ~a)."
+                                  pid))
+                        (format #t "vnstatd is not running.")))))))))))
+
+(define (vnstat-account-service config)
+  (match-record config <vnstat-configuration> (daemon-group daemon-user)
+    (if (every maybe-value-set? (list daemon-group daemon-user))
+        (list
+         (user-group
+          (name daemon-group)
+          (system? #t))
+         (user-account
+          (name daemon-user)
+          (group daemon-group)
+          (system? #t)
+          (home-directory "/var/empty")
+          (shell (file-append shadow "/sbin/nologin"))))
+        '())))
+
+(define vnstat-service-type
+  (service-type
+   (name 'vnstat)
+   (description "vnStat network-traffic monitor service.")
+   (extensions
+    (list (service-extension shepherd-root-service-type
+                             (compose list vnstat-shepherd-service))
+          (service-extension account-service-type
+                             vnstat-account-service)))
+   (default-value (vnstat-configuration))))
+
 \f
 ;;;
 ;;; Zabbix server
-- 
2.38.1





^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH v5] services: Add vnstat-service-type.
  2023-02-10 13:15 ` [bug#60788] [PATCH v5] " Bruno Victal
@ 2023-02-10 14:07   ` Maxim Cournoyer
  0 siblings, 0 replies; 27+ messages in thread
From: Maxim Cournoyer @ 2023-02-10 14:07 UTC (permalink / raw)
  To: Bruno Victal; +Cc: 60788

Hi!

Bruno Victal <mirai@makinata.eu> writes:

[...]

> +            vnstat-configuration
> +            vnstat-configuration?
> +            vnstat-service-type
> +            vnstat-configuration-package
> +            vnstat-configuration-database-dir
> +            vnstat-configuration-5-minute-hours
> +            vnstat-configuration-64bit-interface-counters
> +            vnstat-configuration-always-add-new-interfaces?
> +            vnstat-configuration-bandwidth-detection?
> +            vnstat-configuration-bandwidth-detection-interval
> +            vnstat-configuration-boot-variation
> +            vnstat-configuration-check-disk-space?
> +            vnstat-configuration-create-dirs?
> +            vnstat-configuration-daemon-group
> +            vnstat-configuration-daemon-user
> +            vnstat-configuration-daily-days
> +            vnstat-configuration-database-synchronous
> +            vnstat-configuration-database-write-ahead-logging?
> +            vnstat-configuration-hourly-days
> +            vnstat-configuration-log-file
> +            vnstat-configuration-max-bandwidth
> +            vnstat-configuration-max-BW

^ Looks like you forgot to adjust to use the now all lowercase symbol
here.

> +            vnstat-configuration-monthly-months
> +            vnstat-configuration-month-rotate
> +            vnstat-configuration-month-rotate-affects-years?
> +            vnstat-configuration-offline-save-interval
> +            vnstat-configuration-pid-file
> +            vnstat-configuration-poll-interval
> +            vnstat-configuration-rescan-database-on-save?
> +            vnstat-configuration-save-interval
> +            vnstat-configuration-save-on-status-change?
> +            vnstat-configuration-time-sync-wait
> +            vnstat-configuration-top-day-entries
> +            vnstat-configuration-trafficless-entries?
> +            vnstat-configuration-update-file-owner?
> +            vnstat-configuration-update-interval
> +            vnstat-configuration-use-logging
> +            vnstat-configuration-use-UTC?

Ditto.

The rest LGTM.

-- 
Thanks,
Maxim




^ permalink raw reply	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH v6] services: Add vnstat-service-type.
  2023-01-13 20:07 [bug#60788] [PATCH] services: Add vnstat-service-type Bruno Victal
                   ` (6 preceding siblings ...)
  2023-02-10 13:15 ` [bug#60788] [PATCH v5] " Bruno Victal
@ 2023-02-10 14:14 ` Bruno Victal
  2023-03-22 16:15 ` [bug#60788] [PATCH v7] " Bruno Victal
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 27+ messages in thread
From: Bruno Victal @ 2023-02-10 14:14 UTC (permalink / raw)
  To: 60788; +Cc: Bruno Victal, maxim.cournoyer

* gnu/services/monitoring.scm (vnstat-service-type): New variable.
* doc/guix.texi (Monitoring Services): Document it.
---
 doc/guix.texi               | 246 ++++++++++++++++++-
 gnu/services/monitoring.scm | 457 ++++++++++++++++++++++++++++++++++++
 2 files changed, 701 insertions(+), 2 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index 5fb5850a6c..c54e9e5beb 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -109,11 +109,10 @@
 Copyright @copyright{} 2022 Simon Streit@*
 Copyright @copyright{} 2022 (@*
 Copyright @copyright{} 2022 John Kehayias@*
-Copyright @copyright{} 2022 Bruno Victal@*
+Copyright @copyright{} 2022⁠–⁠2023 Bruno Victal@*
 Copyright @copyright{} 2022 Ivan Vilata-i-Balaguer@*
 Copyright @copyright{} 2023 Giacomo Leidi@*
 Copyright @copyright{} 2022 Antero Mejr@*
-Copyright @copyright{} 2023 Bruno Victal@*
 
 Permission is granted to copy, distribute and/or modify this document
 under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -28241,6 +28240,249 @@ Monitoring Services
 @end table
 @end deftp
 
+@anchor{vnstat}
+@subsubheading vnStat Network Traffic Monitor
+@cindex vnstat
+
+vnStat is a network traffic monitor that uses interface statistics provided
+by the kernel rather than traffic sniffing.  This makes it a light resource
+monitor, regardless of network traffic rate.
+
+@defvar vnstat-service-type
+This is the service type for the @uref{https://humdi.net/vnstat/,vnStat} daemon
+and accepts a @code{vnstat-configuration} value.
+
+The following example will configure the service with default values:
+
+@lisp
+(service vnstat-service-type)
+@end lisp
+@end defvar
+
+@c %start of fragment
+@deftp {Data Type} vnstat-configuration
+Available @code{vnstat-configuration} fields are:
+
+@table @asis
+@item @code{package} (default: @code{vnstat}) (type: file-like)
+The vnstat package.
+
+@item @code{database-dir} (default: @code{"/var/lib/vnstat"}) (type: string)
+Specifies the directory where the database is to be stored.  A full path
+must be given and a leading '/' isn't required.
+
+@item @code{5-minute-hours} (default: @code{48}) (type: maybe-integer)
+Data retention duration for the 5 minute resolution entries.  The
+configuration defines for how many past hours entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@item @code{64bit-interface-counters} (default: @code{-2}) (type: maybe-integer)
+Select interface counter handling.  Set to @code{1} for defining that
+all interfaces use 64-bit counters on the kernel side and @code{0} for
+defining 32-bit counter.  Set to @code{-1} for using the old style logic
+used in earlier versions where counter values within 32-bits are assumed
+to be 32-bit and anything larger is assumed to be a 64-bit counter.  This
+may produce false results if a 64-bit counter is reset within the
+32-bits.  Set to @code{-2} for using automatic detection based on
+available kernel datastructures.
+
+@item @code{always-add-new-interfaces?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable automatic creation of new database entries for
+interfaces not currently in the database even if the database file
+already exists when the daemon is started.  New database entries will
+also get created for new interfaces seen while the daemon is running.
+Pseudo interfaces @samp{lo}, @samp{lo0} and @samp{sit0} are always excluded from getting
+added.
+
+@item @code{bandwidth-detection?} (default: @code{#t}) (type: maybe-boolean)
+Try to automatically detect @var{max-bandwidth} value for each monitored
+interface.  Mostly only ethernet interfaces support this feature.
+@var{max-bandwidth} will be used as fallback value if detection fails.
+Any interface specific @var{max-BW} configuration will disable the
+detection for the specified interface.  In Linux, the detection is
+disabled for tun interfaces due to the Linux kernel always reporting 10
+Mbit regardless of the used real interface.
+
+@item @code{bandwidth-detection-interval} (default: @code{5}) (type: maybe-integer)
+How often in minutes interface specific detection of @var{max-bandwidth}
+is done for detecting possible changes when @var{bandwidth-detection} is
+enabled.  Can be disabled by setting to @code{0}.  Value range:
+@samp{0}..@samp{30}
+
+@item @code{boot-variation} (default: @code{15}) (type: maybe-integer)
+Time in seconds how much the boot time reported by system kernel can
+variate between updates.  Value range: @samp{0}..@samp{300}
+
+@item @code{check-disk-space?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the availability check of at least some free disk
+space before a database write.
+
+@item @code{create-dirs?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the creation of directories when a configured path
+doesn't exist.  This includes @var{database-dir}.
+
+@item @code{daemon-group} (type: maybe-string)
+Specify the group to which the daemon process should switch during
+startup.  The group can either be the name of the group or a numerical
+group id.  Leave empty to disable group switching.  This option can only
+be used when the process is started as root.
+
+@item @code{daemon-user} (type: maybe-string)
+Specify the user to which the daemon process should switch during
+startup.  The user can either be the login of the user or a numerical
+user id.  Leave empty to disable user switching.  This option can only
+be used when the process is started as root.
+
+@item @code{daily-days} (default: @code{62}) (type: maybe-integer)
+Data retention duration for the one day resolution entries.  The
+configuration defines for how many past days entries will be stored.  Set
+to @code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{database-synchronous} (default: @code{-1}) (type: maybe-integer)
+Change the setting of the SQLite "synchronous" flag which controls how
+much care is taken to ensure disk writes have fully completed when
+writing data to the database before continuing other actions.  Higher
+values take extra steps to ensure data safety at the cost of slower
+performance.  A value of @code{0} will result in all handling being left
+to the filesystem itself.  Set to @code{-1} to select the default value
+according to database mode controlled by
+@var{database-write-ahead-logging} setting.  See SQLite documentation
+for more details regarding values from @code{1} to @code{3}.  Value
+range: @samp{-1}..@samp{3}
+
+@item @code{database-write-ahead-logging?} (default: @code{#f}) (type: maybe-boolean)
+Enable or disable SQLite Write-Ahead Logging mode for the database.  See
+SQLite documentation for more details and note that support for
+read-only operations isn't available in older SQLite versions.
+
+@item @code{hourly-days} (default: @code{4}) (type: maybe-integer)
+Data retention duration for the one hour resolution entries.  The
+configuration defines for how many past days entries will be stored.  Set
+to @code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{log-file} (type: maybe-string)
+Specify log file path and name to be used if @var{use-logging} is set to
+@code{1}.
+
+@item @code{max-bandwidth} (type: maybe-integer)
+Maximum bandwidth for all interfaces.  If the interface specific traffic
+exceeds the given value then the data is assumed to be invalid and
+rejected.  Set to 0 in order to disable the feature.  Value range:
+@samp{0}..@samp{50000}
+
+@item @code{max-bw} (type: maybe-alist)
+Same as @var{max-bandwidth} but can be used for setting individual
+limits for selected interfaces.  This is an association list of
+interfaces as symbols/strings to integer values.  For example,
+@lisp
+(max-bw `((eth0 .  15000)
+          (ppp0 .  10000)))
+@end lisp
+@var{bandwidth-detection} is disabled on an interface specific level for
+each @var{max-bw} configuration.  Value range: @samp{0}..@samp{50000}
+
+@item @code{monthly-months} (default: @code{25}) (type: maybe-integer)
+Data retention duration for the one month resolution entries.  The
+configuration defines for how many past months entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@item @code{month-rotate} (default: @code{1}) (type: maybe-integer)
+Day of month that months are expected to change.  Usually set to 1 but
+can be set to alternative values for example for tracking monthly billed
+traffic where the billing period doesn't start on the first day.  For
+example, if set to 7, days of February up to and including the 6th will
+count for January.  Changing this option will not cause existing data to
+be recalculated.  Value range: @samp{1}..@samp{28}
+
+@item @code{month-rotate-affects-years?} (default: @code{#f}) (type: maybe-boolean)
+Enable or disable @var{month-rotate} also affecting yearly data.
+Applicable only when @var{month-rotate} has a value greater than one.
+
+@item @code{offline-save-interval} (default: @code{30}) (type: maybe-integer)
+How often in minutes cached interface data is saved to file when all
+monitored interfaces are offline.  Value range:
+@var{save-interval}..@samp{60}
+
+@item @code{pid-file} (default: @code{"/var/run/vnstatd.pid"}) (type: maybe-string)
+Specify pid file path and name to be used.
+
+@item @code{poll-interval} (default: @code{5}) (type: maybe-integer)
+How often in seconds interfaces are checked for status changes.  Value
+range: @samp{2}..@samp{60}
+
+@item @code{rescan-database-on-save?} (type: maybe-boolean)
+Automatically discover added interfaces from the database and start
+monitoring.  The rescan is done every @var{save-interval} or
+@var{offline-save-interval} minutes depending on the current activity
+state.
+
+@item @code{save-interval} (default: @code{5}) (type: maybe-integer)
+How often in minutes cached interface data is saved to file.  Value
+range: ( @var{update-interval} / 60 )..@samp{60}
+
+@item @code{save-on-status-change?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the additional saving to file of cached interface data
+when the availability of an interface changes, i.e., when an interface
+goes offline or comes online.
+
+@item @code{time-sync-wait} (default: @code{5}) (type: maybe-integer)
+How many minutes to wait during daemon startup for system clock to sync
+if most recent database update appears to be in the future.  This may be
+needed in systems without a real-time clock (RTC) which require some
+time after boot to query and set the correct time.  @code{0} = wait
+disabled.  Value range: @samp{0}..@samp{60}
+
+@item @code{top-day-entries} (default: @code{20}) (type: maybe-integer)
+Data retention duration for the top day entries.  The configuration
+defines how many of the past top day entries will be stored.  Set to
+@code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{trafficless-entries?} (default: @code{#t}) (type: maybe-boolean)
+Create database entries even when there is no traffic during the entry's
+time period.
+
+@item @code{update-file-owner?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the update of file ownership during daemon process
+startup.  During daemon startup, only database, log and pid files will
+be modified if the user or group change feature ( @var{daemon-user} or
+@var{daemon-group} ) is enabled and the files don't match the requested
+user or group.  During manual database creation, this option will cause
+file ownership to be inherited from the database directory if the
+directory already exists.  This option only has effect when the process
+is started as root or via sudo.
+
+@item @code{update-interval} (default: @code{20}) (type: maybe-integer)
+How often in seconds the interface data is updated.  Value range:
+@var{poll-interval}..@samp{300}
+
+@item @code{use-logging} (default: @code{2}) (type: maybe-integer)
+Enable or disable logging.  This option is ignored when the daemon is
+started with .B "-n, --nodaemon" which results in all log output being
+shown in terminal the daemon process is using.  @code{0} = disabled,
+@code{1} = logfile and @code{2} = syslog.
+
+@item @code{use-utc?} (type: maybe-boolean)
+Enable or disable using UTC as timezone in the database for all entries.
+When enabled, all entries added to the database will use UTC regardless
+of the configured system timezone.  When disabled, the configured system
+timezone will be used.  Changing this setting will not result in already
+existing data to be modified.
+
+@item @code{yearly-years} (default: @code{-1}) (type: maybe-integer)
+Data retention duration for the one year resolution entries.  The
+configuration defines for how many past years entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@end table
+@end deftp
+@c %end of fragment
+
 @subsubheading Zabbix server
 @cindex zabbix zabbix-server
 Zabbix is a high performance monitoring system that can collect data from a
diff --git a/gnu/services/monitoring.scm b/gnu/services/monitoring.scm
index 44e2e8886c..ef88c82357 100644
--- a/gnu/services/monitoring.scm
+++ b/gnu/services/monitoring.scm
@@ -3,6 +3,7 @@
 ;;; Copyright © 2018, 2019 Gábor Boskovits <boskovits@gmail.com>
 ;;; Copyright © 2018, 2019, 2020 Oleg Pykhalov <go.wigust@gmail.com>
 ;;; Copyright © 2022 Marius Bakke <marius@gnu.org>
+;;; Copyright © 2023 Bruno Victal <mirai@makinata.eu>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -26,6 +27,7 @@ (define-module (gnu services monitoring)
   #:use-module (gnu services web)
   #:use-module (gnu packages admin)
   #:use-module (gnu packages monitoring)
+  #:use-module (gnu packages networking)
   #:use-module (gnu system shadow)
   #:use-module (guix gexp)
   #:use-module (guix packages)
@@ -34,6 +36,7 @@ (define-module (gnu services monitoring)
   #:use-module ((guix ui) #:select (display-hint G_))
   #:use-module (ice-9 match)
   #:use-module (ice-9 rdelim)
+  #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-26)
   #:use-module (srfi srfi-35)
   #:export (darkstat-configuration
@@ -45,6 +48,46 @@ (define-module (gnu services monitoring)
             prometheus-node-exporter-web-listen-address
             prometheus-node-exporter-service-type
 
+            vnstat-configuration
+            vnstat-configuration?
+            vnstat-service-type
+            vnstat-configuration-package
+            vnstat-configuration-database-dir
+            vnstat-configuration-5-minute-hours
+            vnstat-configuration-64bit-interface-counters
+            vnstat-configuration-always-add-new-interfaces?
+            vnstat-configuration-bandwidth-detection?
+            vnstat-configuration-bandwidth-detection-interval
+            vnstat-configuration-boot-variation
+            vnstat-configuration-check-disk-space?
+            vnstat-configuration-create-dirs?
+            vnstat-configuration-daemon-group
+            vnstat-configuration-daemon-user
+            vnstat-configuration-daily-days
+            vnstat-configuration-database-synchronous
+            vnstat-configuration-database-write-ahead-logging?
+            vnstat-configuration-hourly-days
+            vnstat-configuration-log-file
+            vnstat-configuration-max-bandwidth
+            vnstat-configuration-max-bw
+            vnstat-configuration-monthly-months
+            vnstat-configuration-month-rotate
+            vnstat-configuration-month-rotate-affects-years?
+            vnstat-configuration-offline-save-interval
+            vnstat-configuration-pid-file
+            vnstat-configuration-poll-interval
+            vnstat-configuration-rescan-database-on-save?
+            vnstat-configuration-save-interval
+            vnstat-configuration-save-on-status-change?
+            vnstat-configuration-time-sync-wait
+            vnstat-configuration-top-day-entries
+            vnstat-configuration-trafficless-entries?
+            vnstat-configuration-update-file-owner?
+            vnstat-configuration-update-interval
+            vnstat-configuration-use-logging
+            vnstat-configuration-use-utc?
+            vnstat-configuration-yearly-years
+
             zabbix-server-configuration
             zabbix-server-service-type
             zabbix-agent-configuration
@@ -196,6 +239,420 @@ (define prometheus-node-exporter-service-type
                         prometheus-node-exporter-shepherd-service)))
    (default-value (prometheus-node-exporter-configuration))))
 
+\f
+;;;
+;;; vnstat daemon
+;;;
+
+(define* (camelfy-field-name field-name #:key (dromedary? #f))
+  (match (string-split (symbol->string field-name) #\-)
+    ((head tail ...)
+     (string-join (cons (if dromedary? head (string-upcase head 0 1))
+                        (map (cut string-upcase <> 0 1) tail)) ""))))
+
+(define (strip-trailing-?-character field-name)
+  "Drop rightmost '?' character"
+  (let ((str (symbol->string field-name)))
+    (if (string-suffix? "?" str)
+        (string->symbol (string-drop-right str 1))
+        field-name)))
+
+(define (vnstat-serialize-string field-name value)
+  #~(format #f "~a ~s~%"
+            #$(camelfy-field-name field-name)
+            #$value))
+
+(define vnstat-serialize-integer vnstat-serialize-string)
+
+(define (vnstat-serialize-boolean field-name value)
+  #~(format #f "~a ~a~%"
+            #$(camelfy-field-name (strip-trailing-?-character field-name))
+            #$(if value 1 0)))
+
+(define (vnstat-serialize-alist field-name value)
+  (generic-serialize-alist string-append
+                           (lambda (iface val)
+                             (vnstat-serialize-integer
+                              (format #f "MaxBW~a" iface) val))
+                           value))
+
+(define-maybe string  (prefix vnstat-))
+(define-maybe integer (prefix vnstat-))
+(define-maybe boolean (prefix vnstat-))
+(define-maybe alist   (prefix vnstat-))
+
+;; Documentation strings from vnstat.conf manpage adapted to texinfo.
+;; vnstat checkout: v2.10, commit b3408af1c609aa6265d296cab7bfe59a61d7cf70
+;; Do not reflow these strings or drop the initial \ escape as it makes it
+;; harder to diff against the manpage.
+(define-configuration vnstat-configuration
+  (package
+    (file-like vnstat)
+    "The vnstat package."
+    empty-serializer)
+
+  (database-dir
+   (string "/var/lib/vnstat")
+   "\
+Specifies the directory where the database is to be stored.
+A full path must be given and a leading '/' isn't required.")  
+
+  (5-minute-hours
+   (maybe-integer 48)
+   "\
+Data retention duration for the 5 minute resolution entries. The configuration
+defines for how many past hours entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (64bit-interface-counters
+   (maybe-integer -2)
+   "\
+Select interface counter handling. Set to @code{1} for defining that all interfaces
+use 64-bit counters on the kernel side and @code{0} for defining 32-bit counter. Set
+to @code{-1} for using the old style logic used in earlier versions where counter
+values within 32-bits are assumed to be 32-bit and anything larger is assumed to
+be a 64-bit counter. This may produce false results if a 64-bit counter is
+reset within the 32-bits. Set to @code{-2} for using automatic detection based on
+available kernel datastructures.")
+
+  (always-add-new-interfaces?
+   (maybe-boolean #t)
+   "\
+Enable or disable automatic creation of new database entries for interfaces not
+currently in the database even if the database file already exists when the
+daemon is started. New database entries will also get created for new interfaces
+seen while the daemon is running. Pseudo interfaces @samp{lo}, @samp{lo0} and @samp{sit0} are always
+excluded from getting added.")
+
+  (bandwidth-detection?
+   (maybe-boolean #t)
+   "\
+Try to automatically detect
+@var{max-bandwidth}
+value for each monitored interface. Mostly only ethernet interfaces support
+this feature.
+@var{max-bandwidth}
+will be used as fallback value if detection fails. Any interface specific
+@var{max-BW}
+configuration will disable the detection for the specified interface.
+In Linux, the detection is disabled for tun interfaces due to the
+Linux kernel always reporting 10 Mbit regardless of the used real interface.")
+
+  (bandwidth-detection-interval
+   (maybe-integer 5)
+   "\
+How often in minutes interface specific detection of
+@var{max-bandwidth}
+is done for detecting possible changes when
+@var{bandwidth-detection}
+is enabled. Can be disabled by setting to @code{0}. Value range: @samp{0}..@samp{30}")
+
+  (boot-variation
+   (maybe-integer 15)
+   "\
+Time in seconds how much the boot time reported by system kernel can variate
+between updates. Value range: @samp{0}..@samp{300}")
+
+  (check-disk-space?
+   (maybe-boolean #t)
+   "\
+Enable or disable the availability check of at least some free disk space before
+a database write.")
+
+  (create-dirs?
+   (maybe-boolean #t)
+   "\
+Enable or disable the creation of directories when a configured path doesn't
+exist. This includes @var{database-dir}.")
+
+  (daemon-group
+   maybe-string
+   "\
+Specify the group to which the daemon process should switch during startup.
+The group can either be the name of the group or a numerical group id.
+Leave empty to disable group switching. This option can only be used when
+the process is started as root.")
+
+  (daemon-user
+   maybe-string
+   "\
+Specify the user to which the daemon process should switch during startup.
+The user can either be the login of the user or a numerical user id.
+Leave empty to disable user switching. This option can only be used when
+the process is started as root.")
+
+  (daily-days
+   (maybe-integer 62)
+   "\
+Data retention duration for the one day resolution entries. The configuration
+defines for how many past days entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (database-synchronous
+   (maybe-integer -1)
+   "\
+Change the setting of the SQLite \"synchronous\" flag which controls how much
+care is taken to ensure disk writes have fully completed when writing data to
+the database before continuing other actions. Higher values take extra steps
+to ensure data safety at the cost of slower performance. A value of @code{0} will
+result in all handling being left to the filesystem itself. Set to @code{-1} to
+select the default value according to database mode controlled by
+@var{database-write-ahead-logging}
+setting. See SQLite documentation for more details regarding values from @code{1}
+to @code{3}. Value range: @samp{-1}..@samp{3}")
+
+  (database-write-ahead-logging?
+   (maybe-boolean #f)
+   "\
+Enable or disable SQLite Write-Ahead Logging mode for the database. See SQLite
+documentation for more details and note that support for read-only operations
+isn't available in older SQLite versions.")
+
+  (hourly-days
+   (maybe-integer 4)
+   "\
+Data retention duration for the one hour resolution entries. The configuration
+defines for how many past days entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (log-file
+   maybe-string
+   "\
+Specify log file path and name to be used if @var{use-logging} is set to @code{1}.")
+
+  (max-bandwidth
+   maybe-integer
+   "\
+Maximum bandwidth for all interfaces. If the interface specific traffic
+exceeds the given value then the data is assumed to be invalid and rejected.
+Set to 0 in order to disable the feature. Value range: @samp{0}..@samp{50000}")
+
+  ;; documentation adapted for alist type
+  (max-bw
+   maybe-alist
+   "\
+Same as
+@var{max-bandwidth}
+but can be used for setting individual limits
+for selected interfaces. This is an association list of interfaces
+as symbols/strings to integer values. For example,
+@lisp
+(max-bw
+ `((eth0 . 15000)
+   (ppp0 . 10000)))
+@end lisp
+@var{bandwidth-detection}
+is disabled on an interface specific level for each
+@var{max-bw}
+configuration. Value range: @samp{0}..@samp{50000}"
+   (lambda (field-name value)
+     (if (maybe-value-set? value)
+         (vnstat-serialize-alist field-name value) "")))
+
+  (monthly-months
+   (maybe-integer 25)
+   "\
+Data retention duration for the one month resolution entries. The configuration
+defines for how many past months entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (month-rotate
+   (maybe-integer 1)
+   "\
+Day of month that months are expected to change. Usually set to
+1 but can be set to alternative values for example for tracking
+monthly billed traffic where the billing period doesn't start on
+the first day. For example, if set to 7, days of February up to and
+including the 6th will count for January. Changing this option will
+not cause existing data to be recalculated. Value range: @samp{1}..@samp{28}")
+
+  (month-rotate-affects-years?
+   (maybe-boolean #f)
+   "\
+Enable or disable
+@var{month-rotate}
+also affecting yearly data. Applicable only when
+@var{month-rotate}
+has a value greater than one.")
+
+  (offline-save-interval
+   (maybe-integer 30)
+   "\
+How often in minutes cached interface data is saved to file when all monitored
+interfaces are offline. Value range:
+@var{save-interval}..@samp{60}")
+
+  (pid-file
+   (maybe-string "/var/run/vnstatd.pid")
+   "\
+Specify pid file path and name to be used.")
+
+  (poll-interval
+   (maybe-integer 5)
+   "\
+How often in seconds interfaces are checked for status changes.
+Value range: @samp{2}..@samp{60}")
+
+  (rescan-database-on-save?
+   maybe-boolean
+   "\
+Automatically discover added interfaces from the database and start monitoring.
+The rescan is done every
+@var{save-interval}
+or
+@var{offline-save-interval}
+minutes depending on the current activity state.")
+
+  (save-interval
+   (maybe-integer 5)
+   "\
+How often in minutes cached interface data is saved to file.
+Value range: (
+@var{update-interval} / 60 )..@samp{60}")
+
+  (save-on-status-change?
+   (maybe-boolean #t)
+   "\
+Enable or disable the additional saving to file of cached interface data
+when the availability of an interface changes, i.e., when an interface goes
+offline or comes online.")
+
+  (time-sync-wait
+   (maybe-integer 5)
+   "\
+How many minutes to wait during daemon startup for system clock to sync if
+most recent database update appears to be in the future. This may be needed
+in systems without a real-time clock (RTC) which require some time after boot
+to query and set the correct time. @code{0} = wait disabled.
+Value range: @samp{0}..@samp{60}")
+
+  (top-day-entries
+   (maybe-integer 20)
+   "\
+Data retention duration for the top day entries. The configuration
+defines how many of the past top day entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (trafficless-entries?
+   (maybe-boolean #t)
+   "\
+Create database entries even when there is no traffic during the entry's time
+period.")
+
+  (update-file-owner?
+   (maybe-boolean #t)
+   "\
+Enable or disable the update of file ownership during daemon process startup.
+During daemon startup, only database, log and pid files will be modified if the
+user or group change feature (
+@var{daemon-user}
+or
+@var{daemon-group}
+) is enabled and the files don't match the requested user or group. During manual
+database creation, this option will cause file ownership to be inherited from the
+database directory if the directory already exists. This option only has effect
+when the process is started as root or via sudo.")
+
+  (update-interval
+   (maybe-integer 20)
+   "\
+How often in seconds the interface data is updated. Value range:
+@var{poll-interval}..@samp{300}")
+
+  (use-logging
+   (maybe-integer 2)
+   "\
+Enable or disable logging. This option is ignored when the daemon is started with
+.B \"-n, --nodaemon\"
+which results in all log output being shown in terminal the daemon process is using.
+@code{0} = disabled, @code{1} = logfile and @code{2} = syslog.")
+
+  (use-utc?
+   maybe-boolean
+   "\
+Enable or disable using UTC as timezone in the database for all entries. When
+enabled, all entries added to the database will use UTC regardless of the
+configured system timezone. When disabled, the configured system timezone
+will be used. Changing this setting will not result in already existing
+data to be modified."
+   (lambda (_ value)
+     (if (maybe-value-set? value)
+         (vnstat-serialize-boolean 'use-UTC value) "")))
+
+  (yearly-years
+   (maybe-integer -1)
+   "\
+Data retention duration for the one year resolution entries. The configuration
+defines for how many past years entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (prefix vnstat-))
+
+(define (vnstat-serialize-configuration config)
+  (mixed-text-file
+   "vnstat.conf"
+   (serialize-configuration config vnstat-configuration-fields)))
+
+(define (vnstat-shepherd-service config)
+  (let ((config-file (vnstat-serialize-configuration config)))
+    (match-record config <vnstat-configuration> (package pid-file)
+      (shepherd-service
+       (documentation "Run vnstatd.")
+       (requirement `(networking))
+       (provision '(vnstatd))
+       (start #~(make-forkexec-constructor
+                 (list #$(file-append package "/sbin/vnstatd")
+                       "--daemon"
+                       "--config" #$config-file)
+                 #:pid-file #$pid-file))
+       (stop #~(make-kill-destructor))
+       (actions
+        (list (shepherd-configuration-action config-file)
+              (shepherd-action
+               (name 'reload)
+               (documentation "Reload vnstatd.")
+               (procedure
+                #~(lambda (pid)
+                    (if pid
+                        (begin
+                          (kill pid SIGHUP)
+                          (format #t
+                                  "Issued SIGHUP to vnstatd (PID ~a)."
+                                  pid))
+                        (format #t "vnstatd is not running.")))))))))))
+
+(define (vnstat-account-service config)
+  (match-record config <vnstat-configuration> (daemon-group daemon-user)
+    (if (every maybe-value-set? (list daemon-group daemon-user))
+        (list
+         (user-group
+          (name daemon-group)
+          (system? #t))
+         (user-account
+          (name daemon-user)
+          (group daemon-group)
+          (system? #t)
+          (home-directory "/var/empty")
+          (shell (file-append shadow "/sbin/nologin"))))
+        '())))
+
+(define vnstat-service-type
+  (service-type
+   (name 'vnstat)
+   (description "vnStat network-traffic monitor service.")
+   (extensions
+    (list (service-extension shepherd-root-service-type
+                             (compose list vnstat-shepherd-service))
+          (service-extension account-service-type
+                             vnstat-account-service)))
+   (default-value (vnstat-configuration))))
+
 \f
 ;;;
 ;;; Zabbix server
-- 
2.38.1





^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH v7] services: Add vnstat-service-type.
  2023-01-13 20:07 [bug#60788] [PATCH] services: Add vnstat-service-type Bruno Victal
                   ` (7 preceding siblings ...)
  2023-02-10 14:14 ` [bug#60788] [PATCH v6] " Bruno Victal
@ 2023-03-22 16:15 ` Bruno Victal
  2023-04-03 14:14 ` [bug#60788] [PATCH v8] " Bruno Victal
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 27+ messages in thread
From: Bruno Victal @ 2023-03-22 16:15 UTC (permalink / raw)
  To: 60788; +Cc: Bruno Victal, maxim.cournoyer

* gnu/services/monitoring.scm (vnstat-service-type): New variable.
* doc/guix.texi (Monitoring Services): Document it.
---
 doc/guix.texi               | 239 ++++++++++++++++++
 gnu/services/monitoring.scm | 465 ++++++++++++++++++++++++++++++++++++
 2 files changed, 704 insertions(+)

diff --git a/doc/guix.texi b/doc/guix.texi
index dfdb26103a..9353f06f07 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -28566,6 +28566,245 @@ Monitoring Services
 @end table
 @end deftp
 
+@anchor{vnstat}
+@subsubheading vnStat Network Traffic Monitor
+@cindex vnstat
+
+vnStat is a network traffic monitor that uses interface statistics provided
+by the kernel rather than traffic sniffing.  This makes it a light resource
+monitor, regardless of network traffic rate.
+
+@defvar vnstat-service-type
+This is the service type for the @uref{https://humdi.net/vnstat/,vnStat} daemon
+and accepts a @code{vnstat-configuration} value.
+
+The following example will configure the service with default values:
+
+@lisp
+(service vnstat-service-type)
+@end lisp
+@end defvar
+
+@c %start of fragment
+@deftp {Data Type} vnstat-configuration
+Available @code{vnstat-configuration} fields are:
+
+@table @asis
+@item @code{package} (default: @code{vnstat}) (type: file-like)
+The vnstat package.
+
+@item @code{database-dir} (default: @code{"/var/lib/vnstat"}) (type: string)
+Specifies the directory where the database is to be stored.  A full path
+must be given and a leading '/' isn't required.
+
+@item @code{5-minute-hours} (default: @code{48}) (type: maybe-integer)
+Data retention duration for the 5 minute resolution entries.  The
+configuration defines for how many past hours entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@item @code{64bit-interface-counters} (default: @code{-2}) (type: maybe-integer)
+Select interface counter handling.  Set to @code{1} for defining that
+all interfaces use 64-bit counters on the kernel side and @code{0} for
+defining 32-bit counter.  Set to @code{-1} for using the old style logic
+used in earlier versions where counter values within 32-bits are assumed
+to be 32-bit and anything larger is assumed to be a 64-bit counter.  This
+may produce false results if a 64-bit counter is reset within the
+32-bits.  Set to @code{-2} for using automatic detection based on
+available kernel datastructures.
+
+@item @code{always-add-new-interfaces?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable automatic creation of new database entries for
+interfaces not currently in the database even if the database file
+already exists when the daemon is started.  New database entries will
+also get created for new interfaces seen while the daemon is running.
+Pseudo interfaces @samp{lo}, @samp{lo0} and @samp{sit0} are always excluded from getting
+added.
+
+@item @code{bandwidth-detection?} (default: @code{#t}) (type: maybe-boolean)
+Try to automatically detect @var{max-bandwidth} value for each monitored
+interface.  Mostly only ethernet interfaces support this feature.
+@var{max-bandwidth} will be used as fallback value if detection fails.
+Any interface specific @var{max-BW} configuration will disable the
+detection for the specified interface.  In Linux, the detection is
+disabled for tun interfaces due to the Linux kernel always reporting 10
+Mbit regardless of the used real interface.
+
+@item @code{bandwidth-detection-interval} (default: @code{5}) (type: maybe-integer)
+How often in minutes interface specific detection of @var{max-bandwidth}
+is done for detecting possible changes when @var{bandwidth-detection} is
+enabled.  Can be disabled by setting to @code{0}.  Value range:
+@samp{0}..@samp{30}
+
+@item @code{boot-variation} (default: @code{15}) (type: maybe-integer)
+Time in seconds how much the boot time reported by system kernel can
+variate between updates.  Value range: @samp{0}..@samp{300}
+
+@item @code{check-disk-space?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the availability check of at least some free disk
+space before a database write.
+
+@item @code{create-dirs?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the creation of directories when a configured path
+doesn't exist.  This includes @var{database-dir}.
+
+@item @code{daemon-group} (type: maybe-user-group)
+Specify the group to which the daemon process should switch during
+startup.  Set to @code{%unset-value} to disable group switching.
+
+@item @code{daemon-user} (type: maybe-user-account)
+Specify the user to which the daemon process should switch during
+startup.  Set to @code{%unset-value} to disable user switching.
+
+@item @code{daily-days} (default: @code{62}) (type: maybe-integer)
+Data retention duration for the one day resolution entries.  The
+configuration defines for how many past days entries will be stored.  Set
+to @code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{database-synchronous} (default: @code{-1}) (type: maybe-integer)
+Change the setting of the SQLite "synchronous" flag which controls how
+much care is taken to ensure disk writes have fully completed when
+writing data to the database before continuing other actions.  Higher
+values take extra steps to ensure data safety at the cost of slower
+performance.  A value of @code{0} will result in all handling being left
+to the filesystem itself.  Set to @code{-1} to select the default value
+according to database mode controlled by
+@var{database-write-ahead-logging} setting.  See SQLite documentation
+for more details regarding values from @code{1} to @code{3}.  Value
+range: @samp{-1}..@samp{3}
+
+@item @code{database-write-ahead-logging?} (default: @code{#f}) (type: maybe-boolean)
+Enable or disable SQLite Write-Ahead Logging mode for the database.  See
+SQLite documentation for more details and note that support for
+read-only operations isn't available in older SQLite versions.
+
+@item @code{hourly-days} (default: @code{4}) (type: maybe-integer)
+Data retention duration for the one hour resolution entries.  The
+configuration defines for how many past days entries will be stored.  Set
+to @code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{log-file} (type: maybe-string)
+Specify log file path and name to be used if @var{use-logging} is set to
+@code{1}.
+
+@item @code{max-bandwidth} (type: maybe-integer)
+Maximum bandwidth for all interfaces.  If the interface specific traffic
+exceeds the given value then the data is assumed to be invalid and
+rejected.  Set to 0 in order to disable the feature.  Value range:
+@samp{0}..@samp{50000}
+
+@item @code{max-bw} (type: maybe-alist)
+Same as @var{max-bandwidth} but can be used for setting individual
+limits for selected interfaces.  This is an association list of
+interfaces as symbols/strings to integer values.  For example,
+@lisp
+(max-bw `((eth0 .  15000)
+          (ppp0 .  10000)))
+@end lisp
+@var{bandwidth-detection} is disabled on an interface specific level for
+each @var{max-bw} configuration.  Value range: @samp{0}..@samp{50000}
+
+@item @code{monthly-months} (default: @code{25}) (type: maybe-integer)
+Data retention duration for the one month resolution entries.  The
+configuration defines for how many past months entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@item @code{month-rotate} (default: @code{1}) (type: maybe-integer)
+Day of month that months are expected to change.  Usually set to 1 but
+can be set to alternative values for example for tracking monthly billed
+traffic where the billing period doesn't start on the first day.  For
+example, if set to 7, days of February up to and including the 6th will
+count for January.  Changing this option will not cause existing data to
+be recalculated.  Value range: @samp{1}..@samp{28}
+
+@item @code{month-rotate-affects-years?} (default: @code{#f}) (type: maybe-boolean)
+Enable or disable @var{month-rotate} also affecting yearly data.
+Applicable only when @var{month-rotate} has a value greater than one.
+
+@item @code{offline-save-interval} (default: @code{30}) (type: maybe-integer)
+How often in minutes cached interface data is saved to file when all
+monitored interfaces are offline.  Value range:
+@var{save-interval}..@samp{60}
+
+@item @code{pid-file} (default: @code{"/var/run/vnstatd.pid"}) (type: maybe-string)
+Specify pid file path and name to be used.
+
+@item @code{poll-interval} (default: @code{5}) (type: maybe-integer)
+How often in seconds interfaces are checked for status changes.  Value
+range: @samp{2}..@samp{60}
+
+@item @code{rescan-database-on-save?} (type: maybe-boolean)
+Automatically discover added interfaces from the database and start
+monitoring.  The rescan is done every @var{save-interval} or
+@var{offline-save-interval} minutes depending on the current activity
+state.
+
+@item @code{save-interval} (default: @code{5}) (type: maybe-integer)
+How often in minutes cached interface data is saved to file.  Value
+range: ( @var{update-interval} / 60 )..@samp{60}
+
+@item @code{save-on-status-change?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the additional saving to file of cached interface data
+when the availability of an interface changes, i.e., when an interface
+goes offline or comes online.
+
+@item @code{time-sync-wait} (default: @code{5}) (type: maybe-integer)
+How many minutes to wait during daemon startup for system clock to sync
+if most recent database update appears to be in the future.  This may be
+needed in systems without a real-time clock (RTC) which require some
+time after boot to query and set the correct time.  @code{0} = wait
+disabled.  Value range: @samp{0}..@samp{60}
+
+@item @code{top-day-entries} (default: @code{20}) (type: maybe-integer)
+Data retention duration for the top day entries.  The configuration
+defines how many of the past top day entries will be stored.  Set to
+@code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{trafficless-entries?} (default: @code{#t}) (type: maybe-boolean)
+Create database entries even when there is no traffic during the entry's
+time period.
+
+@item @code{update-file-owner?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the update of file ownership during daemon process
+startup.  During daemon startup, only database, log and pid files will
+be modified if the user or group change feature ( @var{daemon-user} or
+@var{daemon-group} ) is enabled and the files don't match the requested
+user or group.  During manual database creation, this option will cause
+file ownership to be inherited from the database directory if the
+directory already exists.  This option only has effect when the process
+is started as root or via sudo.
+
+@item @code{update-interval} (default: @code{20}) (type: maybe-integer)
+How often in seconds the interface data is updated.  Value range:
+@var{poll-interval}..@samp{300}
+
+@item @code{use-logging} (default: @code{2}) (type: maybe-integer)
+Enable or disable logging.  This option is ignored when the daemon is
+started with .B "-n, --nodaemon" which results in all log output being
+shown in terminal the daemon process is using.  @code{0} = disabled,
+@code{1} = logfile and @code{2} = syslog.
+
+@item @code{use-utc?} (type: maybe-boolean)
+Enable or disable using UTC as timezone in the database for all entries.
+When enabled, all entries added to the database will use UTC regardless
+of the configured system timezone.  When disabled, the configured system
+timezone will be used.  Changing this setting will not result in already
+existing data to be modified.
+
+@item @code{yearly-years} (default: @code{-1}) (type: maybe-integer)
+Data retention duration for the one year resolution entries.  The
+configuration defines for how many past years entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@end table
+@end deftp
+@c %end of fragment
+
 @subsubheading Zabbix server
 @cindex zabbix zabbix-server
 Zabbix is a high performance monitoring system that can collect data from a
diff --git a/gnu/services/monitoring.scm b/gnu/services/monitoring.scm
index bbf8b10f8b..0cfbff0946 100644
--- a/gnu/services/monitoring.scm
+++ b/gnu/services/monitoring.scm
@@ -3,6 +3,7 @@
 ;;; Copyright © 2018, 2019 Gábor Boskovits <boskovits@gmail.com>
 ;;; Copyright © 2018, 2019, 2020 Oleg Pykhalov <go.wigust@gmail.com>
 ;;; Copyright © 2022 Marius Bakke <marius@gnu.org>
+;;; Copyright © 2023 Bruno Victal <mirai@makinata.eu>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -26,6 +27,7 @@ (define-module (gnu services monitoring)
   #:use-module (gnu services web)
   #:use-module (gnu packages admin)
   #:use-module (gnu packages monitoring)
+  #:use-module (gnu packages networking)
   #:use-module (gnu system shadow)
   #:use-module (guix gexp)
   #:use-module (guix packages)
@@ -34,6 +36,7 @@ (define-module (gnu services monitoring)
   #:use-module ((guix ui) #:select (display-hint G_))
   #:use-module (ice-9 match)
   #:use-module (ice-9 rdelim)
+  #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-26)
   #:use-module (srfi srfi-35)
   #:export (darkstat-configuration
@@ -45,6 +48,46 @@ (define-module (gnu services monitoring)
             prometheus-node-exporter-web-listen-address
             prometheus-node-exporter-service-type
 
+            vnstat-configuration
+            vnstat-configuration?
+            vnstat-service-type
+            vnstat-configuration-package
+            vnstat-configuration-database-dir
+            vnstat-configuration-5-minute-hours
+            vnstat-configuration-64bit-interface-counters
+            vnstat-configuration-always-add-new-interfaces?
+            vnstat-configuration-bandwidth-detection?
+            vnstat-configuration-bandwidth-detection-interval
+            vnstat-configuration-boot-variation
+            vnstat-configuration-check-disk-space?
+            vnstat-configuration-create-dirs?
+            vnstat-configuration-daemon-group
+            vnstat-configuration-daemon-user
+            vnstat-configuration-daily-days
+            vnstat-configuration-database-synchronous
+            vnstat-configuration-database-write-ahead-logging?
+            vnstat-configuration-hourly-days
+            vnstat-configuration-log-file
+            vnstat-configuration-max-bandwidth
+            vnstat-configuration-max-bw
+            vnstat-configuration-monthly-months
+            vnstat-configuration-month-rotate
+            vnstat-configuration-month-rotate-affects-years?
+            vnstat-configuration-offline-save-interval
+            vnstat-configuration-pid-file
+            vnstat-configuration-poll-interval
+            vnstat-configuration-rescan-database-on-save?
+            vnstat-configuration-save-interval
+            vnstat-configuration-save-on-status-change?
+            vnstat-configuration-time-sync-wait
+            vnstat-configuration-top-day-entries
+            vnstat-configuration-trafficless-entries?
+            vnstat-configuration-update-file-owner?
+            vnstat-configuration-update-interval
+            vnstat-configuration-use-logging
+            vnstat-configuration-use-utc?
+            vnstat-configuration-yearly-years
+
             zabbix-server-configuration
             zabbix-server-service-type
             zabbix-agent-configuration
@@ -196,6 +239,428 @@ (define prometheus-node-exporter-service-type
                         prometheus-node-exporter-shepherd-service)))
    (default-value (prometheus-node-exporter-configuration))))
 
+\f
+;;;
+;;; vnstat daemon
+;;;
+
+(define* (camelfy-field-name field-name #:key (dromedary? #f))
+  (match (string-split (symbol->string field-name) #\-)
+    ((head tail ...)
+     (string-join (cons (if dromedary? head (string-upcase head 0 1))
+                        (map (cut string-upcase <> 0 1) tail)) ""))))
+
+(define (strip-trailing-?-character field-name)
+  "Drop rightmost '?' character"
+  (let ((str (symbol->string field-name)))
+    (if (string-suffix? "?" str)
+        (string->symbol (string-drop-right str 1))
+        field-name)))
+
+(define (vnstat-serialize-string field-name value)
+  #~(format #f "~a ~s~%"
+            #$(camelfy-field-name field-name)
+            #$value))
+
+(define vnstat-serialize-integer vnstat-serialize-string)
+
+(define (vnstat-serialize-boolean field-name value)
+  #~(format #f "~a ~a~%"
+            #$(camelfy-field-name (strip-trailing-?-character field-name))
+            #$(if value 1 0)))
+
+(define (vnstat-serialize-alist field-name value)
+  (generic-serialize-alist string-append
+                           (lambda (iface val)
+                             (vnstat-serialize-integer
+                              (format #f "MaxBW~a" iface) val))
+                           value))
+
+(define (vnstat-serialize-user-account field-name value)
+  (vnstat-serialize-string field-name (user-account-name value)))
+
+(define (vnstat-serialize-user-group field-name value)
+  (vnstat-serialize-string field-name (user-group-name value)))
+
+(define-maybe string  (prefix vnstat-))
+(define-maybe integer (prefix vnstat-))
+(define-maybe boolean (prefix vnstat-))
+(define-maybe alist   (prefix vnstat-))
+(define-maybe user-account (prefix vnstat-))
+(define-maybe user-group (prefix vnstat-))
+
+(define %vnstat-user
+  (user-account
+   (name "vnstat")
+   (group "vnstat")
+   (system? #t)
+   (home-directory "/var/empty")
+   (shell (file-append shadow "/sbin/nologin"))))
+
+(define %vnstat-group
+  (user-group
+   (name "vnstat")
+   (system? #t)))
+
+;; Documentation strings from vnstat.conf manpage adapted to texinfo.
+;; vnstat checkout: v2.10, commit b3408af1c609aa6265d296cab7bfe59a61d7cf70
+;; Do not reflow these strings or drop the initial \ escape as it makes it
+;; harder to diff against the manpage.
+(define-configuration vnstat-configuration
+  (package
+    (file-like vnstat)
+    "The vnstat package."
+    empty-serializer)
+
+  (database-dir
+   (string "/var/lib/vnstat")
+   "\
+Specifies the directory where the database is to be stored.
+A full path must be given and a leading '/' isn't required.")  
+
+  (5-minute-hours
+   (maybe-integer 48)
+   "\
+Data retention duration for the 5 minute resolution entries. The configuration
+defines for how many past hours entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (64bit-interface-counters
+   (maybe-integer -2)
+   "\
+Select interface counter handling. Set to @code{1} for defining that all interfaces
+use 64-bit counters on the kernel side and @code{0} for defining 32-bit counter. Set
+to @code{-1} for using the old style logic used in earlier versions where counter
+values within 32-bits are assumed to be 32-bit and anything larger is assumed to
+be a 64-bit counter. This may produce false results if a 64-bit counter is
+reset within the 32-bits. Set to @code{-2} for using automatic detection based on
+available kernel datastructures.")
+
+  (always-add-new-interfaces?
+   (maybe-boolean #t)
+   "\
+Enable or disable automatic creation of new database entries for interfaces not
+currently in the database even if the database file already exists when the
+daemon is started. New database entries will also get created for new interfaces
+seen while the daemon is running. Pseudo interfaces @samp{lo}, @samp{lo0} and @samp{sit0} are always
+excluded from getting added.")
+
+  (bandwidth-detection?
+   (maybe-boolean #t)
+   "\
+Try to automatically detect
+@var{max-bandwidth}
+value for each monitored interface. Mostly only ethernet interfaces support
+this feature.
+@var{max-bandwidth}
+will be used as fallback value if detection fails. Any interface specific
+@var{max-BW}
+configuration will disable the detection for the specified interface.
+In Linux, the detection is disabled for tun interfaces due to the
+Linux kernel always reporting 10 Mbit regardless of the used real interface.")
+
+  (bandwidth-detection-interval
+   (maybe-integer 5)
+   "\
+How often in minutes interface specific detection of
+@var{max-bandwidth}
+is done for detecting possible changes when
+@var{bandwidth-detection}
+is enabled. Can be disabled by setting to @code{0}. Value range: @samp{0}..@samp{30}")
+
+  (boot-variation
+   (maybe-integer 15)
+   "\
+Time in seconds how much the boot time reported by system kernel can variate
+between updates. Value range: @samp{0}..@samp{300}")
+
+  (check-disk-space?
+   (maybe-boolean #t)
+   "\
+Enable or disable the availability check of at least some free disk space before
+a database write.")
+
+  (create-dirs?
+   (maybe-boolean #t)
+   "\
+Enable or disable the creation of directories when a configured path doesn't
+exist. This includes @var{database-dir}.")
+
+  ;; Note: Documentation for daemon-group and daemon-user adapted
+  ;; for user-group and user-account record-types.
+  (daemon-group
+   (maybe-user-group %vnstat-group)
+   "\
+Specify the group to which the daemon process should switch during startup.
+Set to @code{%unset-value} to disable group switching.")
+
+  (daemon-user
+   (maybe-user-account %vnstat-user)
+   "\
+Specify the user to which the daemon process should switch during startup.
+Set to @code{%unset-value} to disable user switching.")
+
+  (daily-days
+   (maybe-integer 62)
+   "\
+Data retention duration for the one day resolution entries. The configuration
+defines for how many past days entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (database-synchronous
+   (maybe-integer -1)
+   "\
+Change the setting of the SQLite \"synchronous\" flag which controls how much
+care is taken to ensure disk writes have fully completed when writing data to
+the database before continuing other actions. Higher values take extra steps
+to ensure data safety at the cost of slower performance. A value of @code{0} will
+result in all handling being left to the filesystem itself. Set to @code{-1} to
+select the default value according to database mode controlled by
+@var{database-write-ahead-logging}
+setting. See SQLite documentation for more details regarding values from @code{1}
+to @code{3}. Value range: @samp{-1}..@samp{3}")
+
+  (database-write-ahead-logging?
+   (maybe-boolean #f)
+   "\
+Enable or disable SQLite Write-Ahead Logging mode for the database. See SQLite
+documentation for more details and note that support for read-only operations
+isn't available in older SQLite versions.")
+
+  (hourly-days
+   (maybe-integer 4)
+   "\
+Data retention duration for the one hour resolution entries. The configuration
+defines for how many past days entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (log-file
+   maybe-string
+   "\
+Specify log file path and name to be used if @var{use-logging} is set to @code{1}.")
+
+  (max-bandwidth
+   maybe-integer
+   "\
+Maximum bandwidth for all interfaces. If the interface specific traffic
+exceeds the given value then the data is assumed to be invalid and rejected.
+Set to 0 in order to disable the feature. Value range: @samp{0}..@samp{50000}")
+
+  ;; documentation adapted for alist type
+  (max-bw
+   maybe-alist
+   "\
+Same as
+@var{max-bandwidth}
+but can be used for setting individual limits
+for selected interfaces. This is an association list of interfaces
+as symbols/strings to integer values. For example,
+@lisp
+(max-bw
+ `((eth0 . 15000)
+   (ppp0 . 10000)))
+@end lisp
+@var{bandwidth-detection}
+is disabled on an interface specific level for each
+@var{max-bw}
+configuration. Value range: @samp{0}..@samp{50000}"
+   (lambda (field-name value)
+     (if (maybe-value-set? value)
+         (vnstat-serialize-alist field-name value) "")))
+
+  (monthly-months
+   (maybe-integer 25)
+   "\
+Data retention duration for the one month resolution entries. The configuration
+defines for how many past months entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (month-rotate
+   (maybe-integer 1)
+   "\
+Day of month that months are expected to change. Usually set to
+1 but can be set to alternative values for example for tracking
+monthly billed traffic where the billing period doesn't start on
+the first day. For example, if set to 7, days of February up to and
+including the 6th will count for January. Changing this option will
+not cause existing data to be recalculated. Value range: @samp{1}..@samp{28}")
+
+  (month-rotate-affects-years?
+   (maybe-boolean #f)
+   "\
+Enable or disable
+@var{month-rotate}
+also affecting yearly data. Applicable only when
+@var{month-rotate}
+has a value greater than one.")
+
+  (offline-save-interval
+   (maybe-integer 30)
+   "\
+How often in minutes cached interface data is saved to file when all monitored
+interfaces are offline. Value range:
+@var{save-interval}..@samp{60}")
+
+  (pid-file
+   (maybe-string "/var/run/vnstat/vnstatd.pid")
+   "\
+Specify pid file path and name to be used.")
+
+  (poll-interval
+   (maybe-integer 5)
+   "\
+How often in seconds interfaces are checked for status changes.
+Value range: @samp{2}..@samp{60}")
+
+  (rescan-database-on-save?
+   maybe-boolean
+   "\
+Automatically discover added interfaces from the database and start monitoring.
+The rescan is done every
+@var{save-interval}
+or
+@var{offline-save-interval}
+minutes depending on the current activity state.")
+
+  (save-interval
+   (maybe-integer 5)
+   "\
+How often in minutes cached interface data is saved to file.
+Value range: (
+@var{update-interval} / 60 )..@samp{60}")
+
+  (save-on-status-change?
+   (maybe-boolean #t)
+   "\
+Enable or disable the additional saving to file of cached interface data
+when the availability of an interface changes, i.e., when an interface goes
+offline or comes online.")
+
+  (time-sync-wait
+   (maybe-integer 5)
+   "\
+How many minutes to wait during daemon startup for system clock to sync if
+most recent database update appears to be in the future. This may be needed
+in systems without a real-time clock (RTC) which require some time after boot
+to query and set the correct time. @code{0} = wait disabled.
+Value range: @samp{0}..@samp{60}")
+
+  (top-day-entries
+   (maybe-integer 20)
+   "\
+Data retention duration for the top day entries. The configuration
+defines how many of the past top day entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (trafficless-entries?
+   (maybe-boolean #t)
+   "\
+Create database entries even when there is no traffic during the entry's time
+period.")
+
+  (update-file-owner?
+   (maybe-boolean #t)
+   "\
+Enable or disable the update of file ownership during daemon process startup.
+During daemon startup, only database, log and pid files will be modified if the
+user or group change feature (
+@var{daemon-user}
+or
+@var{daemon-group}
+) is enabled and the files don't match the requested user or group. During manual
+database creation, this option will cause file ownership to be inherited from the
+database directory if the directory already exists. This option only has effect
+when the process is started as root or via sudo.")
+
+  (update-interval
+   (maybe-integer 20)
+   "\
+How often in seconds the interface data is updated. Value range:
+@var{poll-interval}..@samp{300}")
+
+  (use-logging
+   (maybe-integer 2)
+   "\
+Enable or disable logging. This option is ignored when the daemon is started with
+.B \"-n, --nodaemon\"
+which results in all log output being shown in terminal the daemon process is using.
+@code{0} = disabled, @code{1} = logfile and @code{2} = syslog.")
+
+  (use-utc?
+   maybe-boolean
+   "\
+Enable or disable using UTC as timezone in the database for all entries. When
+enabled, all entries added to the database will use UTC regardless of the
+configured system timezone. When disabled, the configured system timezone
+will be used. Changing this setting will not result in already existing
+data to be modified."
+   (lambda (_ value)
+     (if (maybe-value-set? value)
+         (vnstat-serialize-boolean 'use-UTC value) "")))
+
+  (yearly-years
+   (maybe-integer -1)
+   "\
+Data retention duration for the one year resolution entries. The configuration
+defines for how many past years entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (prefix vnstat-))
+
+(define (vnstat-serialize-configuration config)
+  (mixed-text-file
+   "vnstat.conf"
+   (serialize-configuration config vnstat-configuration-fields)))
+
+(define (vnstat-shepherd-service config)
+  (let ((config-file (vnstat-serialize-configuration config)))
+    (match-record config <vnstat-configuration> (package pid-file)
+      (shepherd-service
+       (documentation "Run vnstatd.")
+       (requirement `(networking file-systems))
+       (provision '(vnstatd))
+       (start #~(make-forkexec-constructor
+                 (list #$(file-append package "/sbin/vnstatd")
+                       "--daemon"
+                       "--config" #$config-file)
+                 #:pid-file #$pid-file))
+       (stop #~(make-kill-destructor))
+       (actions
+        (list (shepherd-configuration-action config-file)
+              (shepherd-action
+               (name 'reload)
+               (documentation "Reload vnstatd.")
+               (procedure
+                #~(lambda (pid)
+                    (if pid
+                        (begin
+                          (kill pid SIGHUP)
+                          (format #t
+                                  "Issued SIGHUP to vnstatd (PID ~a)."
+                                  pid))
+                        (format #t "vnstatd is not running.")))))))))))
+
+(define (vnstat-account-service config)
+  (match-record config <vnstat-configuration> (daemon-group daemon-user)
+    (filter-map maybe-value (list daemon-group daemon-user))))
+
+(define vnstat-service-type
+  (service-type
+   (name 'vnstat)
+   (description "vnStat network-traffic monitor service.")
+   (extensions
+    (list (service-extension shepherd-root-service-type
+                             (compose list vnstat-shepherd-service))
+          (service-extension account-service-type
+                             vnstat-account-service)))
+   (default-value (vnstat-configuration))))
+
 \f
 ;;;
 ;;; Zabbix server

base-commit: 91f92f32d5a9317dd333d570b54c2ba34c9c14a9
-- 
2.39.1





^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH v8] services: Add vnstat-service-type.
  2023-01-13 20:07 [bug#60788] [PATCH] services: Add vnstat-service-type Bruno Victal
                   ` (8 preceding siblings ...)
  2023-03-22 16:15 ` [bug#60788] [PATCH v7] " Bruno Victal
@ 2023-04-03 14:14 ` Bruno Victal
  2023-04-04 13:08 ` [bug#60788] [PATCH v9] " Bruno Victal
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 27+ messages in thread
From: Bruno Victal @ 2023-04-03 14:14 UTC (permalink / raw)
  To: 60788; +Cc: Bruno Victal, maxim.cournoyer

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[B^[[B^[[B^[[B^[[B^[[B^[[B^[[B^[[B^[[B^[[B^[[B^[[B^[[B^[[B^[[B^[[B^[[B^[[B^[[B^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A^[[A, Size: 31134 bytes --]

* gnu/services/monitoring.scm (vnstat-service-type): New variable.
* doc/guix.texi (Monitoring Services): Document it.
---

Notable changes since v7:
* Adapted to the recent changes in define-configuration.

 doc/guix.texi               | 239 ++++++++++++++++++
 gnu/services/monitoring.scm | 465 ++++++++++++++++++++++++++++++++++++
 2 files changed, 704 insertions(+)

diff --git a/doc/guix.texi b/doc/guix.texi
index 4f72e2f34a..7e69098267 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -28569,6 +28569,245 @@ Monitoring Services
 @end table
 @end deftp
 
+@anchor{vnstat}
+@subsubheading vnStat Network Traffic Monitor
+@cindex vnstat
+
+vnStat is a network traffic monitor that uses interface statistics provided
+by the kernel rather than traffic sniffing.  This makes it a light resource
+monitor, regardless of network traffic rate.
+
+@defvar vnstat-service-type
+This is the service type for the @uref{https://humdi.net/vnstat/,vnStat} daemon
+and accepts a @code{vnstat-configuration} value.
+
+The following example will configure the service with default values:
+
+@lisp
+(service vnstat-service-type)
+@end lisp
+@end defvar
+
+@c %start of fragment
+@deftp {Data Type} vnstat-configuration
+Available @code{vnstat-configuration} fields are:
+
+@table @asis
+@item @code{package} (default: @code{vnstat}) (type: file-like)
+The vnstat package.
+
+@item @code{database-dir} (default: @code{"/var/lib/vnstat"}) (type: string)
+Specifies the directory where the database is to be stored.  A full path
+must be given and a leading '/' isn't required.
+
+@item @code{5-minute-hours} (default: @code{48}) (type: maybe-integer)
+Data retention duration for the 5 minute resolution entries.  The
+configuration defines for how many past hours entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@item @code{64bit-interface-counters} (default: @code{-2}) (type: maybe-integer)
+Select interface counter handling.  Set to @code{1} for defining that
+all interfaces use 64-bit counters on the kernel side and @code{0} for
+defining 32-bit counter.  Set to @code{-1} for using the old style logic
+used in earlier versions where counter values within 32-bits are assumed
+to be 32-bit and anything larger is assumed to be a 64-bit counter.  This
+may produce false results if a 64-bit counter is reset within the
+32-bits.  Set to @code{-2} for using automatic detection based on
+available kernel datastructures.
+
+@item @code{always-add-new-interfaces?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable automatic creation of new database entries for
+interfaces not currently in the database even if the database file
+already exists when the daemon is started.  New database entries will
+also get created for new interfaces seen while the daemon is running.
+Pseudo interfaces @samp{lo}, @samp{lo0} and @samp{sit0} are always excluded from getting
+added.
+
+@item @code{bandwidth-detection?} (default: @code{#t}) (type: maybe-boolean)
+Try to automatically detect @var{max-bandwidth} value for each monitored
+interface.  Mostly only ethernet interfaces support this feature.
+@var{max-bandwidth} will be used as fallback value if detection fails.
+Any interface specific @var{max-BW} configuration will disable the
+detection for the specified interface.  In Linux, the detection is
+disabled for tun interfaces due to the Linux kernel always reporting 10
+Mbit regardless of the used real interface.
+
+@item @code{bandwidth-detection-interval} (default: @code{5}) (type: maybe-integer)
+How often in minutes interface specific detection of @var{max-bandwidth}
+is done for detecting possible changes when @var{bandwidth-detection} is
+enabled.  Can be disabled by setting to @code{0}.  Value range:
+@samp{0}..@samp{30}
+
+@item @code{boot-variation} (default: @code{15}) (type: maybe-integer)
+Time in seconds how much the boot time reported by system kernel can
+variate between updates.  Value range: @samp{0}..@samp{300}
+
+@item @code{check-disk-space?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the availability check of at least some free disk
+space before a database write.
+
+@item @code{create-dirs?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the creation of directories when a configured path
+doesn't exist.  This includes @var{database-dir}.
+
+@item @code{daemon-group} (type: maybe-user-group)
+Specify the group to which the daemon process should switch during
+startup.  Set to @code{%unset-value} to disable group switching.
+
+@item @code{daemon-user} (type: maybe-user-account)
+Specify the user to which the daemon process should switch during
+startup.  Set to @code{%unset-value} to disable user switching.
+
+@item @code{daily-days} (default: @code{62}) (type: maybe-integer)
+Data retention duration for the one day resolution entries.  The
+configuration defines for how many past days entries will be stored.  Set
+to @code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{database-synchronous} (default: @code{-1}) (type: maybe-integer)
+Change the setting of the SQLite "synchronous" flag which controls how
+much care is taken to ensure disk writes have fully completed when
+writing data to the database before continuing other actions.  Higher
+values take extra steps to ensure data safety at the cost of slower
+performance.  A value of @code{0} will result in all handling being left
+to the filesystem itself.  Set to @code{-1} to select the default value
+according to database mode controlled by
+@var{database-write-ahead-logging} setting.  See SQLite documentation
+for more details regarding values from @code{1} to @code{3}.  Value
+range: @samp{-1}..@samp{3}
+
+@item @code{database-write-ahead-logging?} (default: @code{#f}) (type: maybe-boolean)
+Enable or disable SQLite Write-Ahead Logging mode for the database.  See
+SQLite documentation for more details and note that support for
+read-only operations isn't available in older SQLite versions.
+
+@item @code{hourly-days} (default: @code{4}) (type: maybe-integer)
+Data retention duration for the one hour resolution entries.  The
+configuration defines for how many past days entries will be stored.  Set
+to @code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{log-file} (type: maybe-string)
+Specify log file path and name to be used if @var{use-logging} is set to
+@code{1}.
+
+@item @code{max-bandwidth} (type: maybe-integer)
+Maximum bandwidth for all interfaces.  If the interface specific traffic
+exceeds the given value then the data is assumed to be invalid and
+rejected.  Set to 0 in order to disable the feature.  Value range:
+@samp{0}..@samp{50000}
+
+@item @code{max-bw} (type: maybe-alist)
+Same as @var{max-bandwidth} but can be used for setting individual
+limits for selected interfaces.  This is an association list of
+interfaces as symbols/strings to integer values.  For example,
+@lisp
+(max-bw `((eth0 .  15000)
+          (ppp0 .  10000)))
+@end lisp
+@var{bandwidth-detection} is disabled on an interface specific level for
+each @var{max-bw} configuration.  Value range: @samp{0}..@samp{50000}
+
+@item @code{monthly-months} (default: @code{25}) (type: maybe-integer)
+Data retention duration for the one month resolution entries.  The
+configuration defines for how many past months entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@item @code{month-rotate} (default: @code{1}) (type: maybe-integer)
+Day of month that months are expected to change.  Usually set to 1 but
+can be set to alternative values for example for tracking monthly billed
+traffic where the billing period doesn't start on the first day.  For
+example, if set to 7, days of February up to and including the 6th will
+count for January.  Changing this option will not cause existing data to
+be recalculated.  Value range: @samp{1}..@samp{28}
+
+@item @code{month-rotate-affects-years?} (default: @code{#f}) (type: maybe-boolean)
+Enable or disable @var{month-rotate} also affecting yearly data.
+Applicable only when @var{month-rotate} has a value greater than one.
+
+@item @code{offline-save-interval} (default: @code{30}) (type: maybe-integer)
+How often in minutes cached interface data is saved to file when all
+monitored interfaces are offline.  Value range:
+@var{save-interval}..@samp{60}
+
+@item @code{pid-file} (default: @code{"/var/run/vnstatd.pid"}) (type: maybe-string)
+Specify pid file path and name to be used.
+
+@item @code{poll-interval} (default: @code{5}) (type: maybe-integer)
+How often in seconds interfaces are checked for status changes.  Value
+range: @samp{2}..@samp{60}
+
+@item @code{rescan-database-on-save?} (type: maybe-boolean)
+Automatically discover added interfaces from the database and start
+monitoring.  The rescan is done every @var{save-interval} or
+@var{offline-save-interval} minutes depending on the current activity
+state.
+
+@item @code{save-interval} (default: @code{5}) (type: maybe-integer)
+How often in minutes cached interface data is saved to file.  Value
+range: ( @var{update-interval} / 60 )..@samp{60}
+
+@item @code{save-on-status-change?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the additional saving to file of cached interface data
+when the availability of an interface changes, i.e., when an interface
+goes offline or comes online.
+
+@item @code{time-sync-wait} (default: @code{5}) (type: maybe-integer)
+How many minutes to wait during daemon startup for system clock to sync
+if most recent database update appears to be in the future.  This may be
+needed in systems without a real-time clock (RTC) which require some
+time after boot to query and set the correct time.  @code{0} = wait
+disabled.  Value range: @samp{0}..@samp{60}
+
+@item @code{top-day-entries} (default: @code{20}) (type: maybe-integer)
+Data retention duration for the top day entries.  The configuration
+defines how many of the past top day entries will be stored.  Set to
+@code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{trafficless-entries?} (default: @code{#t}) (type: maybe-boolean)
+Create database entries even when there is no traffic during the entry's
+time period.
+
+@item @code{update-file-owner?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the update of file ownership during daemon process
+startup.  During daemon startup, only database, log and pid files will
+be modified if the user or group change feature ( @var{daemon-user} or
+@var{daemon-group} ) is enabled and the files don't match the requested
+user or group.  During manual database creation, this option will cause
+file ownership to be inherited from the database directory if the
+directory already exists.  This option only has effect when the process
+is started as root or via sudo.
+
+@item @code{update-interval} (default: @code{20}) (type: maybe-integer)
+How often in seconds the interface data is updated.  Value range:
+@var{poll-interval}..@samp{300}
+
+@item @code{use-logging} (default: @code{2}) (type: maybe-integer)
+Enable or disable logging.  This option is ignored when the daemon is
+started with .B "-n, --nodaemon" which results in all log output being
+shown in terminal the daemon process is using.  @code{0} = disabled,
+@code{1} = logfile and @code{2} = syslog.
+
+@item @code{use-utc?} (type: maybe-boolean)
+Enable or disable using UTC as timezone in the database for all entries.
+When enabled, all entries added to the database will use UTC regardless
+of the configured system timezone.  When disabled, the configured system
+timezone will be used.  Changing this setting will not result in already
+existing data to be modified.
+
+@item @code{yearly-years} (default: @code{-1}) (type: maybe-integer)
+Data retention duration for the one year resolution entries.  The
+configuration defines for how many past years entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@end table
+@end deftp
+@c %end of fragment
+
 @subsubheading Zabbix server
 @cindex zabbix zabbix-server
 Zabbix is a high performance monitoring system that can collect data from a
diff --git a/gnu/services/monitoring.scm b/gnu/services/monitoring.scm
index bbf8b10f8b..0cfbff0946 100644
--- a/gnu/services/monitoring.scm
+++ b/gnu/services/monitoring.scm
@@ -3,6 +3,7 @@
 ;;; Copyright © 2018, 2019 Gábor Boskovits <boskovits@gmail.com>
 ;;; Copyright © 2018, 2019, 2020 Oleg Pykhalov <go.wigust@gmail.com>
 ;;; Copyright © 2022 Marius Bakke <marius@gnu.org>
+;;; Copyright © 2023 Bruno Victal <mirai@makinata.eu>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -26,6 +27,7 @@ (define-module (gnu services monitoring)
   #:use-module (gnu services web)
   #:use-module (gnu packages admin)
   #:use-module (gnu packages monitoring)
+  #:use-module (gnu packages networking)
   #:use-module (gnu system shadow)
   #:use-module (guix gexp)
   #:use-module (guix packages)
@@ -34,6 +36,7 @@ (define-module (gnu services monitoring)
   #:use-module ((guix ui) #:select (display-hint G_))
   #:use-module (ice-9 match)
   #:use-module (ice-9 rdelim)
+  #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-26)
   #:use-module (srfi srfi-35)
   #:export (darkstat-configuration
@@ -45,6 +48,46 @@ (define-module (gnu services monitoring)
             prometheus-node-exporter-web-listen-address
             prometheus-node-exporter-service-type
 
+            vnstat-configuration
+            vnstat-configuration?
+            vnstat-service-type
+            vnstat-configuration-package
+            vnstat-configuration-database-dir
+            vnstat-configuration-5-minute-hours
+            vnstat-configuration-64bit-interface-counters
+            vnstat-configuration-always-add-new-interfaces?
+            vnstat-configuration-bandwidth-detection?
+            vnstat-configuration-bandwidth-detection-interval
+            vnstat-configuration-boot-variation
+            vnstat-configuration-check-disk-space?
+            vnstat-configuration-create-dirs?
+            vnstat-configuration-daemon-group
+            vnstat-configuration-daemon-user
+            vnstat-configuration-daily-days
+            vnstat-configuration-database-synchronous
+            vnstat-configuration-database-write-ahead-logging?
+            vnstat-configuration-hourly-days
+            vnstat-configuration-log-file
+            vnstat-configuration-max-bandwidth
+            vnstat-configuration-max-bw
+            vnstat-configuration-monthly-months
+            vnstat-configuration-month-rotate
+            vnstat-configuration-month-rotate-affects-years?
+            vnstat-configuration-offline-save-interval
+            vnstat-configuration-pid-file
+            vnstat-configuration-poll-interval
+            vnstat-configuration-rescan-database-on-save?
+            vnstat-configuration-save-interval
+            vnstat-configuration-save-on-status-change?
+            vnstat-configuration-time-sync-wait
+            vnstat-configuration-top-day-entries
+            vnstat-configuration-trafficless-entries?
+            vnstat-configuration-update-file-owner?
+            vnstat-configuration-update-interval
+            vnstat-configuration-use-logging
+            vnstat-configuration-use-utc?
+            vnstat-configuration-yearly-years
+
             zabbix-server-configuration
             zabbix-server-service-type
             zabbix-agent-configuration
@@ -196,6 +239,428 @@ (define prometheus-node-exporter-service-type
                         prometheus-node-exporter-shepherd-service)))
    (default-value (prometheus-node-exporter-configuration))))
 
+\f
+;;;
+;;; vnstat daemon
+;;;
+
+(define* (camelfy-field-name field-name #:key (dromedary? #f))
+  (match (string-split (symbol->string field-name) #\-)
+    ((head tail ...)
+     (string-join (cons (if dromedary? head (string-upcase head 0 1))
+                        (map (cut string-upcase <> 0 1) tail)) ""))))
+
+(define (strip-trailing-?-character field-name)
+  "Drop rightmost '?' character"
+  (let ((str (symbol->string field-name)))
+    (if (string-suffix? "?" str)
+        (string->symbol (string-drop-right str 1))
+        field-name)))
+
+(define (vnstat-serialize-string field-name value)
+  #~(format #f "~a ~s~%"
+            #$(camelfy-field-name field-name)
+            #$value))
+
+(define vnstat-serialize-integer vnstat-serialize-string)
+
+(define (vnstat-serialize-boolean field-name value)
+  #~(format #f "~a ~a~%"
+            #$(camelfy-field-name (strip-trailing-?-character field-name))
+            #$(if value 1 0)))
+
+(define (vnstat-serialize-alist field-name value)
+  (generic-serialize-alist string-append
+                           (lambda (iface val)
+                             (vnstat-serialize-integer
+                              (format #f "MaxBW~a" iface) val))
+                           value))
+
+(define (vnstat-serialize-user-account field-name value)
+  (vnstat-serialize-string field-name (user-account-name value)))
+
+(define (vnstat-serialize-user-group field-name value)
+  (vnstat-serialize-string field-name (user-group-name value)))
+
+(define-maybe string  (prefix vnstat-))
+(define-maybe integer (prefix vnstat-))
+(define-maybe boolean (prefix vnstat-))
+(define-maybe alist   (prefix vnstat-))
+(define-maybe user-account (prefix vnstat-))
+(define-maybe user-group (prefix vnstat-))
+
+(define %vnstat-user
+  (user-account
+   (name "vnstat")
+   (group "vnstat")
+   (system? #t)
+   (home-directory "/var/empty")
+   (shell (file-append shadow "/sbin/nologin"))))
+
+(define %vnstat-group
+  (user-group
+   (name "vnstat")
+   (system? #t)))
+
+;; Documentation strings from vnstat.conf manpage adapted to texinfo.
+;; vnstat checkout: v2.10, commit b3408af1c609aa6265d296cab7bfe59a61d7cf70
+;; Do not reflow these strings or drop the initial \ escape as it makes it
+;; harder to diff against the manpage.
+(define-configuration vnstat-configuration
+  (package
+    (file-like vnstat)
+    "The vnstat package."
+    empty-serializer)
+
+  (database-dir
+   (string "/var/lib/vnstat")
+   "\
+Specifies the directory where the database is to be stored.
+A full path must be given and a leading '/' isn't required.")  
+
+  (5-minute-hours
+   (maybe-integer 48)
+   "\
+Data retention duration for the 5 minute resolution entries. The configuration
+defines for how many past hours entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (64bit-interface-counters
+   (maybe-integer -2)
+   "\
+Select interface counter handling. Set to @code{1} for defining that all interfaces
+use 64-bit counters on the kernel side and @code{0} for defining 32-bit counter. Set
+to @code{-1} for using the old style logic used in earlier versions where counter
+values within 32-bits are assumed to be 32-bit and anything larger is assumed to
+be a 64-bit counter. This may produce false results if a 64-bit counter is
+reset within the 32-bits. Set to @code{-2} for using automatic detection based on
+available kernel datastructures.")
+
+  (always-add-new-interfaces?
+   (maybe-boolean #t)
+   "\
+Enable or disable automatic creation of new database entries for interfaces not
+currently in the database even if the database file already exists when the
+daemon is started. New database entries will also get created for new interfaces
+seen while the daemon is running. Pseudo interfaces @samp{lo}, @samp{lo0} and @samp{sit0} are always
+excluded from getting added.")
+
+  (bandwidth-detection?
+   (maybe-boolean #t)
+   "\
+Try to automatically detect
+@var{max-bandwidth}
+value for each monitored interface. Mostly only ethernet interfaces support
+this feature.
+@var{max-bandwidth}
+will be used as fallback value if detection fails. Any interface specific
+@var{max-BW}
+configuration will disable the detection for the specified interface.
+In Linux, the detection is disabled for tun interfaces due to the
+Linux kernel always reporting 10 Mbit regardless of the used real interface.")
+
+  (bandwidth-detection-interval
+   (maybe-integer 5)
+   "\
+How often in minutes interface specific detection of
+@var{max-bandwidth}
+is done for detecting possible changes when
+@var{bandwidth-detection}
+is enabled. Can be disabled by setting to @code{0}. Value range: @samp{0}..@samp{30}")
+
+  (boot-variation
+   (maybe-integer 15)
+   "\
+Time in seconds how much the boot time reported by system kernel can variate
+between updates. Value range: @samp{0}..@samp{300}")
+
+  (check-disk-space?
+   (maybe-boolean #t)
+   "\
+Enable or disable the availability check of at least some free disk space before
+a database write.")
+
+  (create-dirs?
+   (maybe-boolean #t)
+   "\
+Enable or disable the creation of directories when a configured path doesn't
+exist. This includes @var{database-dir}.")
+
+  ;; Note: Documentation for daemon-group and daemon-user adapted
+  ;; for user-group and user-account record-types.
+  (daemon-group
+   (maybe-user-group %vnstat-group)
+   "\
+Specify the group to which the daemon process should switch during startup.
+Set to @code{%unset-value} to disable group switching.")
+
+  (daemon-user
+   (maybe-user-account %vnstat-user)
+   "\
+Specify the user to which the daemon process should switch during startup.
+Set to @code{%unset-value} to disable user switching.")
+
+  (daily-days
+   (maybe-integer 62)
+   "\
+Data retention duration for the one day resolution entries. The configuration
+defines for how many past days entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (database-synchronous
+   (maybe-integer -1)
+   "\
+Change the setting of the SQLite \"synchronous\" flag which controls how much
+care is taken to ensure disk writes have fully completed when writing data to
+the database before continuing other actions. Higher values take extra steps
+to ensure data safety at the cost of slower performance. A value of @code{0} will
+result in all handling being left to the filesystem itself. Set to @code{-1} to
+select the default value according to database mode controlled by
+@var{database-write-ahead-logging}
+setting. See SQLite documentation for more details regarding values from @code{1}
+to @code{3}. Value range: @samp{-1}..@samp{3}")
+
+  (database-write-ahead-logging?
+   (maybe-boolean #f)
+   "\
+Enable or disable SQLite Write-Ahead Logging mode for the database. See SQLite
+documentation for more details and note that support for read-only operations
+isn't available in older SQLite versions.")
+
+  (hourly-days
+   (maybe-integer 4)
+   "\
+Data retention duration for the one hour resolution entries. The configuration
+defines for how many past days entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (log-file
+   maybe-string
+   "\
+Specify log file path and name to be used if @var{use-logging} is set to @code{1}.")
+
+  (max-bandwidth
+   maybe-integer
+   "\
+Maximum bandwidth for all interfaces. If the interface specific traffic
+exceeds the given value then the data is assumed to be invalid and rejected.
+Set to 0 in order to disable the feature. Value range: @samp{0}..@samp{50000}")
+
+  ;; documentation adapted for alist type
+  (max-bw
+   maybe-alist
+   "\
+Same as
+@var{max-bandwidth}
+but can be used for setting individual limits
+for selected interfaces. This is an association list of interfaces
+as symbols/strings to integer values. For example,
+@lisp
+(max-bw
+ `((eth0 . 15000)
+   (ppp0 . 10000)))
+@end lisp
+@var{bandwidth-detection}
+is disabled on an interface specific level for each
+@var{max-bw}
+configuration. Value range: @samp{0}..@samp{50000}"
+   (lambda (field-name value)
+     (if (maybe-value-set? value)
+         (vnstat-serialize-alist field-name value) "")))
+
+  (monthly-months
+   (maybe-integer 25)
+   "\
+Data retention duration for the one month resolution entries. The configuration
+defines for how many past months entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (month-rotate
+   (maybe-integer 1)
+   "\
+Day of month that months are expected to change. Usually set to
+1 but can be set to alternative values for example for tracking
+monthly billed traffic where the billing period doesn't start on
+the first day. For example, if set to 7, days of February up to and
+including the 6th will count for January. Changing this option will
+not cause existing data to be recalculated. Value range: @samp{1}..@samp{28}")
+
+  (month-rotate-affects-years?
+   (maybe-boolean #f)
+   "\
+Enable or disable
+@var{month-rotate}
+also affecting yearly data. Applicable only when
+@var{month-rotate}
+has a value greater than one.")
+
+  (offline-save-interval
+   (maybe-integer 30)
+   "\
+How often in minutes cached interface data is saved to file when all monitored
+interfaces are offline. Value range:
+@var{save-interval}..@samp{60}")
+
+  (pid-file
+   (maybe-string "/var/run/vnstat/vnstatd.pid")
+   "\
+Specify pid file path and name to be used.")
+
+  (poll-interval
+   (maybe-integer 5)
+   "\
+How often in seconds interfaces are checked for status changes.
+Value range: @samp{2}..@samp{60}")
+
+  (rescan-database-on-save?
+   maybe-boolean
+   "\
+Automatically discover added interfaces from the database and start monitoring.
+The rescan is done every
+@var{save-interval}
+or
+@var{offline-save-interval}
+minutes depending on the current activity state.")
+
+  (save-interval
+   (maybe-integer 5)
+   "\
+How often in minutes cached interface data is saved to file.
+Value range: (
+@var{update-interval} / 60 )..@samp{60}")
+
+  (save-on-status-change?
+   (maybe-boolean #t)
+   "\
+Enable or disable the additional saving to file of cached interface data
+when the availability of an interface changes, i.e., when an interface goes
+offline or comes online.")
+
+  (time-sync-wait
+   (maybe-integer 5)
+   "\
+How many minutes to wait during daemon startup for system clock to sync if
+most recent database update appears to be in the future. This may be needed
+in systems without a real-time clock (RTC) which require some time after boot
+to query and set the correct time. @code{0} = wait disabled.
+Value range: @samp{0}..@samp{60}")
+
+  (top-day-entries
+   (maybe-integer 20)
+   "\
+Data retention duration for the top day entries. The configuration
+defines how many of the past top day entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (trafficless-entries?
+   (maybe-boolean #t)
+   "\
+Create database entries even when there is no traffic during the entry's time
+period.")
+
+  (update-file-owner?
+   (maybe-boolean #t)
+   "\
+Enable or disable the update of file ownership during daemon process startup.
+During daemon startup, only database, log and pid files will be modified if the
+user or group change feature (
+@var{daemon-user}
+or
+@var{daemon-group}
+) is enabled and the files don't match the requested user or group. During manual
+database creation, this option will cause file ownership to be inherited from the
+database directory if the directory already exists. This option only has effect
+when the process is started as root or via sudo.")
+
+  (update-interval
+   (maybe-integer 20)
+   "\
+How often in seconds the interface data is updated. Value range:
+@var{poll-interval}..@samp{300}")
+
+  (use-logging
+   (maybe-integer 2)
+   "\
+Enable or disable logging. This option is ignored when the daemon is started with
+.B \"-n, --nodaemon\"
+which results in all log output being shown in terminal the daemon process is using.
+@code{0} = disabled, @code{1} = logfile and @code{2} = syslog.")
+
+  (use-utc?
+   maybe-boolean
+   "\
+Enable or disable using UTC as timezone in the database for all entries. When
+enabled, all entries added to the database will use UTC regardless of the
+configured system timezone. When disabled, the configured system timezone
+will be used. Changing this setting will not result in already existing
+data to be modified."
+   (lambda (_ value)
+     (if (maybe-value-set? value)
+         (vnstat-serialize-boolean 'use-UTC value) "")))
+
+  (yearly-years
+   (maybe-integer -1)
+   "\
+Data retention duration for the one year resolution entries. The configuration
+defines for how many past years entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (prefix vnstat-))
+
+(define (vnstat-serialize-configuration config)
+  (mixed-text-file
+   "vnstat.conf"
+   (serialize-configuration config vnstat-configuration-fields)))
+
+(define (vnstat-shepherd-service config)
+  (let ((config-file (vnstat-serialize-configuration config)))
+    (match-record config <vnstat-configuration> (package pid-file)
+      (shepherd-service
+       (documentation "Run vnstatd.")
+       (requirement `(networking file-systems))
+       (provision '(vnstatd))
+       (start #~(make-forkexec-constructor
+                 (list #$(file-append package "/sbin/vnstatd")
+                       "--daemon"
+                       "--config" #$config-file)
+                 #:pid-file #$pid-file))
+       (stop #~(make-kill-destructor))
+       (actions
+        (list (shepherd-configuration-action config-file)
+              (shepherd-action
+               (name 'reload)
+               (documentation "Reload vnstatd.")
+               (procedure
+                #~(lambda (pid)
+                    (if pid
+                        (begin
+                          (kill pid SIGHUP)
+                          (format #t
+                                  "Issued SIGHUP to vnstatd (PID ~a)."
+                                  pid))
+                        (format #t "vnstatd is not running.")))))))))))
+
+(define (vnstat-account-service config)
+  (match-record config <vnstat-configuration> (daemon-group daemon-user)
+    (filter-map maybe-value (list daemon-group daemon-user))))
+
+(define vnstat-service-type
+  (service-type
+   (name 'vnstat)
+   (description "vnStat network-traffic monitor service.")
+   (extensions
+    (list (service-extension shepherd-root-service-type
+                             (compose list vnstat-shepherd-service))
+          (service-extension account-service-type
+                             vnstat-account-service)))
+   (default-value (vnstat-configuration))))
+
 \f
 ;;;
 ;;; Zabbix server

base-commit: 86d580c82f984fe0a2929a75518fe9ad62fb8cb4
-- 
2.39.2





^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH v9] services: Add vnstat-service-type.
  2023-01-13 20:07 [bug#60788] [PATCH] services: Add vnstat-service-type Bruno Victal
                   ` (9 preceding siblings ...)
  2023-04-03 14:14 ` [bug#60788] [PATCH v8] " Bruno Victal
@ 2023-04-04 13:08 ` Bruno Victal
  2023-04-07 15:22   ` [bug#60788] [PATCH] " Ludovic Courtès
  2023-05-05  0:18 ` [bug#60788] [PATCH v10 1/3] " Bruno Victal
                   ` (2 subsequent siblings)
  13 siblings, 1 reply; 27+ messages in thread
From: Bruno Victal @ 2023-04-04 13:08 UTC (permalink / raw)
  To: 60788; +Cc: Bruno Victal, maxim.cournoyer

* gnu/services/monitoring.scm (vnstat-service-type): New variable.
* doc/guix.texi (Monitoring Services): Document it.
---

Changes since v8:
* Forgot to amend commit in v8, v9 corrects this.

 doc/guix.texi               | 239 ++++++++++++++++++
 gnu/services/monitoring.scm | 467 ++++++++++++++++++++++++++++++++++++
 2 files changed, 706 insertions(+)

diff --git a/doc/guix.texi b/doc/guix.texi
index 4f72e2f34a..7e69098267 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -28569,6 +28569,245 @@ Monitoring Services
 @end table
 @end deftp
 
+@anchor{vnstat}
+@subsubheading vnStat Network Traffic Monitor
+@cindex vnstat
+
+vnStat is a network traffic monitor that uses interface statistics provided
+by the kernel rather than traffic sniffing.  This makes it a light resource
+monitor, regardless of network traffic rate.
+
+@defvar vnstat-service-type
+This is the service type for the @uref{https://humdi.net/vnstat/,vnStat} daemon
+and accepts a @code{vnstat-configuration} value.
+
+The following example will configure the service with default values:
+
+@lisp
+(service vnstat-service-type)
+@end lisp
+@end defvar
+
+@c %start of fragment
+@deftp {Data Type} vnstat-configuration
+Available @code{vnstat-configuration} fields are:
+
+@table @asis
+@item @code{package} (default: @code{vnstat}) (type: file-like)
+The vnstat package.
+
+@item @code{database-dir} (default: @code{"/var/lib/vnstat"}) (type: string)
+Specifies the directory where the database is to be stored.  A full path
+must be given and a leading '/' isn't required.
+
+@item @code{5-minute-hours} (default: @code{48}) (type: maybe-integer)
+Data retention duration for the 5 minute resolution entries.  The
+configuration defines for how many past hours entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@item @code{64bit-interface-counters} (default: @code{-2}) (type: maybe-integer)
+Select interface counter handling.  Set to @code{1} for defining that
+all interfaces use 64-bit counters on the kernel side and @code{0} for
+defining 32-bit counter.  Set to @code{-1} for using the old style logic
+used in earlier versions where counter values within 32-bits are assumed
+to be 32-bit and anything larger is assumed to be a 64-bit counter.  This
+may produce false results if a 64-bit counter is reset within the
+32-bits.  Set to @code{-2} for using automatic detection based on
+available kernel datastructures.
+
+@item @code{always-add-new-interfaces?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable automatic creation of new database entries for
+interfaces not currently in the database even if the database file
+already exists when the daemon is started.  New database entries will
+also get created for new interfaces seen while the daemon is running.
+Pseudo interfaces @samp{lo}, @samp{lo0} and @samp{sit0} are always excluded from getting
+added.
+
+@item @code{bandwidth-detection?} (default: @code{#t}) (type: maybe-boolean)
+Try to automatically detect @var{max-bandwidth} value for each monitored
+interface.  Mostly only ethernet interfaces support this feature.
+@var{max-bandwidth} will be used as fallback value if detection fails.
+Any interface specific @var{max-BW} configuration will disable the
+detection for the specified interface.  In Linux, the detection is
+disabled for tun interfaces due to the Linux kernel always reporting 10
+Mbit regardless of the used real interface.
+
+@item @code{bandwidth-detection-interval} (default: @code{5}) (type: maybe-integer)
+How often in minutes interface specific detection of @var{max-bandwidth}
+is done for detecting possible changes when @var{bandwidth-detection} is
+enabled.  Can be disabled by setting to @code{0}.  Value range:
+@samp{0}..@samp{30}
+
+@item @code{boot-variation} (default: @code{15}) (type: maybe-integer)
+Time in seconds how much the boot time reported by system kernel can
+variate between updates.  Value range: @samp{0}..@samp{300}
+
+@item @code{check-disk-space?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the availability check of at least some free disk
+space before a database write.
+
+@item @code{create-dirs?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the creation of directories when a configured path
+doesn't exist.  This includes @var{database-dir}.
+
+@item @code{daemon-group} (type: maybe-user-group)
+Specify the group to which the daemon process should switch during
+startup.  Set to @code{%unset-value} to disable group switching.
+
+@item @code{daemon-user} (type: maybe-user-account)
+Specify the user to which the daemon process should switch during
+startup.  Set to @code{%unset-value} to disable user switching.
+
+@item @code{daily-days} (default: @code{62}) (type: maybe-integer)
+Data retention duration for the one day resolution entries.  The
+configuration defines for how many past days entries will be stored.  Set
+to @code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{database-synchronous} (default: @code{-1}) (type: maybe-integer)
+Change the setting of the SQLite "synchronous" flag which controls how
+much care is taken to ensure disk writes have fully completed when
+writing data to the database before continuing other actions.  Higher
+values take extra steps to ensure data safety at the cost of slower
+performance.  A value of @code{0} will result in all handling being left
+to the filesystem itself.  Set to @code{-1} to select the default value
+according to database mode controlled by
+@var{database-write-ahead-logging} setting.  See SQLite documentation
+for more details regarding values from @code{1} to @code{3}.  Value
+range: @samp{-1}..@samp{3}
+
+@item @code{database-write-ahead-logging?} (default: @code{#f}) (type: maybe-boolean)
+Enable or disable SQLite Write-Ahead Logging mode for the database.  See
+SQLite documentation for more details and note that support for
+read-only operations isn't available in older SQLite versions.
+
+@item @code{hourly-days} (default: @code{4}) (type: maybe-integer)
+Data retention duration for the one hour resolution entries.  The
+configuration defines for how many past days entries will be stored.  Set
+to @code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{log-file} (type: maybe-string)
+Specify log file path and name to be used if @var{use-logging} is set to
+@code{1}.
+
+@item @code{max-bandwidth} (type: maybe-integer)
+Maximum bandwidth for all interfaces.  If the interface specific traffic
+exceeds the given value then the data is assumed to be invalid and
+rejected.  Set to 0 in order to disable the feature.  Value range:
+@samp{0}..@samp{50000}
+
+@item @code{max-bw} (type: maybe-alist)
+Same as @var{max-bandwidth} but can be used for setting individual
+limits for selected interfaces.  This is an association list of
+interfaces as symbols/strings to integer values.  For example,
+@lisp
+(max-bw `((eth0 .  15000)
+          (ppp0 .  10000)))
+@end lisp
+@var{bandwidth-detection} is disabled on an interface specific level for
+each @var{max-bw} configuration.  Value range: @samp{0}..@samp{50000}
+
+@item @code{monthly-months} (default: @code{25}) (type: maybe-integer)
+Data retention duration for the one month resolution entries.  The
+configuration defines for how many past months entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@item @code{month-rotate} (default: @code{1}) (type: maybe-integer)
+Day of month that months are expected to change.  Usually set to 1 but
+can be set to alternative values for example for tracking monthly billed
+traffic where the billing period doesn't start on the first day.  For
+example, if set to 7, days of February up to and including the 6th will
+count for January.  Changing this option will not cause existing data to
+be recalculated.  Value range: @samp{1}..@samp{28}
+
+@item @code{month-rotate-affects-years?} (default: @code{#f}) (type: maybe-boolean)
+Enable or disable @var{month-rotate} also affecting yearly data.
+Applicable only when @var{month-rotate} has a value greater than one.
+
+@item @code{offline-save-interval} (default: @code{30}) (type: maybe-integer)
+How often in minutes cached interface data is saved to file when all
+monitored interfaces are offline.  Value range:
+@var{save-interval}..@samp{60}
+
+@item @code{pid-file} (default: @code{"/var/run/vnstatd.pid"}) (type: maybe-string)
+Specify pid file path and name to be used.
+
+@item @code{poll-interval} (default: @code{5}) (type: maybe-integer)
+How often in seconds interfaces are checked for status changes.  Value
+range: @samp{2}..@samp{60}
+
+@item @code{rescan-database-on-save?} (type: maybe-boolean)
+Automatically discover added interfaces from the database and start
+monitoring.  The rescan is done every @var{save-interval} or
+@var{offline-save-interval} minutes depending on the current activity
+state.
+
+@item @code{save-interval} (default: @code{5}) (type: maybe-integer)
+How often in minutes cached interface data is saved to file.  Value
+range: ( @var{update-interval} / 60 )..@samp{60}
+
+@item @code{save-on-status-change?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the additional saving to file of cached interface data
+when the availability of an interface changes, i.e., when an interface
+goes offline or comes online.
+
+@item @code{time-sync-wait} (default: @code{5}) (type: maybe-integer)
+How many minutes to wait during daemon startup for system clock to sync
+if most recent database update appears to be in the future.  This may be
+needed in systems without a real-time clock (RTC) which require some
+time after boot to query and set the correct time.  @code{0} = wait
+disabled.  Value range: @samp{0}..@samp{60}
+
+@item @code{top-day-entries} (default: @code{20}) (type: maybe-integer)
+Data retention duration for the top day entries.  The configuration
+defines how many of the past top day entries will be stored.  Set to
+@code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{trafficless-entries?} (default: @code{#t}) (type: maybe-boolean)
+Create database entries even when there is no traffic during the entry's
+time period.
+
+@item @code{update-file-owner?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the update of file ownership during daemon process
+startup.  During daemon startup, only database, log and pid files will
+be modified if the user or group change feature ( @var{daemon-user} or
+@var{daemon-group} ) is enabled and the files don't match the requested
+user or group.  During manual database creation, this option will cause
+file ownership to be inherited from the database directory if the
+directory already exists.  This option only has effect when the process
+is started as root or via sudo.
+
+@item @code{update-interval} (default: @code{20}) (type: maybe-integer)
+How often in seconds the interface data is updated.  Value range:
+@var{poll-interval}..@samp{300}
+
+@item @code{use-logging} (default: @code{2}) (type: maybe-integer)
+Enable or disable logging.  This option is ignored when the daemon is
+started with .B "-n, --nodaemon" which results in all log output being
+shown in terminal the daemon process is using.  @code{0} = disabled,
+@code{1} = logfile and @code{2} = syslog.
+
+@item @code{use-utc?} (type: maybe-boolean)
+Enable or disable using UTC as timezone in the database for all entries.
+When enabled, all entries added to the database will use UTC regardless
+of the configured system timezone.  When disabled, the configured system
+timezone will be used.  Changing this setting will not result in already
+existing data to be modified.
+
+@item @code{yearly-years} (default: @code{-1}) (type: maybe-integer)
+Data retention duration for the one year resolution entries.  The
+configuration defines for how many past years entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@end table
+@end deftp
+@c %end of fragment
+
 @subsubheading Zabbix server
 @cindex zabbix zabbix-server
 Zabbix is a high performance monitoring system that can collect data from a
diff --git a/gnu/services/monitoring.scm b/gnu/services/monitoring.scm
index bbf8b10f8b..09de7807c0 100644
--- a/gnu/services/monitoring.scm
+++ b/gnu/services/monitoring.scm
@@ -3,6 +3,7 @@
 ;;; Copyright © 2018, 2019 Gábor Boskovits <boskovits@gmail.com>
 ;;; Copyright © 2018, 2019, 2020 Oleg Pykhalov <go.wigust@gmail.com>
 ;;; Copyright © 2022 Marius Bakke <marius@gnu.org>
+;;; Copyright © 2023 Bruno Victal <mirai@makinata.eu>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -26,6 +27,7 @@ (define-module (gnu services monitoring)
   #:use-module (gnu services web)
   #:use-module (gnu packages admin)
   #:use-module (gnu packages monitoring)
+  #:use-module (gnu packages networking)
   #:use-module (gnu system shadow)
   #:use-module (guix gexp)
   #:use-module (guix packages)
@@ -34,6 +36,7 @@ (define-module (gnu services monitoring)
   #:use-module ((guix ui) #:select (display-hint G_))
   #:use-module (ice-9 match)
   #:use-module (ice-9 rdelim)
+  #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-26)
   #:use-module (srfi srfi-35)
   #:export (darkstat-configuration
@@ -45,6 +48,46 @@ (define-module (gnu services monitoring)
             prometheus-node-exporter-web-listen-address
             prometheus-node-exporter-service-type
 
+            vnstat-configuration
+            vnstat-configuration?
+            vnstat-service-type
+            vnstat-configuration-package
+            vnstat-configuration-database-dir
+            vnstat-configuration-5-minute-hours
+            vnstat-configuration-64bit-interface-counters
+            vnstat-configuration-always-add-new-interfaces?
+            vnstat-configuration-bandwidth-detection?
+            vnstat-configuration-bandwidth-detection-interval
+            vnstat-configuration-boot-variation
+            vnstat-configuration-check-disk-space?
+            vnstat-configuration-create-dirs?
+            vnstat-configuration-daemon-group
+            vnstat-configuration-daemon-user
+            vnstat-configuration-daily-days
+            vnstat-configuration-database-synchronous
+            vnstat-configuration-database-write-ahead-logging?
+            vnstat-configuration-hourly-days
+            vnstat-configuration-log-file
+            vnstat-configuration-max-bandwidth
+            vnstat-configuration-max-bw
+            vnstat-configuration-monthly-months
+            vnstat-configuration-month-rotate
+            vnstat-configuration-month-rotate-affects-years?
+            vnstat-configuration-offline-save-interval
+            vnstat-configuration-pid-file
+            vnstat-configuration-poll-interval
+            vnstat-configuration-rescan-database-on-save?
+            vnstat-configuration-save-interval
+            vnstat-configuration-save-on-status-change?
+            vnstat-configuration-time-sync-wait
+            vnstat-configuration-top-day-entries
+            vnstat-configuration-trafficless-entries?
+            vnstat-configuration-update-file-owner?
+            vnstat-configuration-update-interval
+            vnstat-configuration-use-logging
+            vnstat-configuration-use-utc?
+            vnstat-configuration-yearly-years
+
             zabbix-server-configuration
             zabbix-server-service-type
             zabbix-agent-configuration
@@ -196,6 +239,430 @@ (define prometheus-node-exporter-service-type
                         prometheus-node-exporter-shepherd-service)))
    (default-value (prometheus-node-exporter-configuration))))
 
+\f
+;;;
+;;; vnstat daemon
+;;;
+
+(define* (camelfy-field-name field-name #:key (dromedary? #f))
+  (match (string-split (symbol->string field-name) #\-)
+    ((head tail ...)
+     (string-join (cons (if dromedary? head (string-upcase head 0 1))
+                        (map (cut string-upcase <> 0 1) tail)) ""))))
+
+(define (strip-trailing-?-character field-name)
+  "Drop rightmost '?' character"
+  (let ((str (symbol->string field-name)))
+    (if (string-suffix? "?" str)
+        (string->symbol (string-drop-right str 1))
+        field-name)))
+
+(define (vnstat-serialize-string field-name value)
+  #~(format #f "~a ~s~%"
+            #$(camelfy-field-name field-name)
+            #$value))
+
+(define vnstat-serialize-integer vnstat-serialize-string)
+
+(define (vnstat-serialize-boolean field-name value)
+  #~(format #f "~a ~a~%"
+            #$(camelfy-field-name (strip-trailing-?-character field-name))
+            #$(if value 1 0)))
+
+(define (vnstat-serialize-alist field-name value)
+  (generic-serialize-alist string-append
+                           (lambda (iface val)
+                             (vnstat-serialize-integer
+                              (format #f "MaxBW~a" iface) val))
+                           value))
+
+(define (vnstat-serialize-user-account field-name value)
+  (vnstat-serialize-string field-name (user-account-name value)))
+
+(define (vnstat-serialize-user-group field-name value)
+  (vnstat-serialize-string field-name (user-group-name value)))
+
+(define-maybe string  (prefix vnstat-))
+(define-maybe integer (prefix vnstat-))
+(define-maybe boolean (prefix vnstat-))
+(define-maybe alist   (prefix vnstat-))
+(define-maybe user-account (prefix vnstat-))
+(define-maybe user-group (prefix vnstat-))
+
+(define %vnstat-user
+  (user-account
+   (name "vnstat")
+   (group "vnstat")
+   (system? #t)
+   (home-directory "/var/empty")
+   (shell (file-append shadow "/sbin/nologin"))))
+
+(define %vnstat-group
+  (user-group
+   (name "vnstat")
+   (system? #t)))
+
+;; Documentation strings from vnstat.conf manpage adapted to texinfo.
+;; vnstat checkout: v2.10, commit b3408af1c609aa6265d296cab7bfe59a61d7cf70
+;; Do not reflow these strings or drop the initial \ escape as it makes it
+;; harder to diff against the manpage.
+(define-configuration vnstat-configuration
+  (package
+    (file-like vnstat)
+    "The vnstat package."
+    empty-serializer)
+
+  (database-dir
+   (string "/var/lib/vnstat")
+   "\
+Specifies the directory where the database is to be stored.
+A full path must be given and a leading '/' isn't required.")  
+
+  (5-minute-hours
+   (maybe-integer 48)
+   "\
+Data retention duration for the 5 minute resolution entries. The configuration
+defines for how many past hours entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (64bit-interface-counters
+   (maybe-integer -2)
+   "\
+Select interface counter handling. Set to @code{1} for defining that all interfaces
+use 64-bit counters on the kernel side and @code{0} for defining 32-bit counter. Set
+to @code{-1} for using the old style logic used in earlier versions where counter
+values within 32-bits are assumed to be 32-bit and anything larger is assumed to
+be a 64-bit counter. This may produce false results if a 64-bit counter is
+reset within the 32-bits. Set to @code{-2} for using automatic detection based on
+available kernel datastructures.")
+
+  (always-add-new-interfaces?
+   (maybe-boolean #t)
+   "\
+Enable or disable automatic creation of new database entries for interfaces not
+currently in the database even if the database file already exists when the
+daemon is started. New database entries will also get created for new interfaces
+seen while the daemon is running. Pseudo interfaces @samp{lo}, @samp{lo0} and @samp{sit0} are always
+excluded from getting added.")
+
+  (bandwidth-detection?
+   (maybe-boolean #t)
+   "\
+Try to automatically detect
+@var{max-bandwidth}
+value for each monitored interface. Mostly only ethernet interfaces support
+this feature.
+@var{max-bandwidth}
+will be used as fallback value if detection fails. Any interface specific
+@var{max-BW}
+configuration will disable the detection for the specified interface.
+In Linux, the detection is disabled for tun interfaces due to the
+Linux kernel always reporting 10 Mbit regardless of the used real interface.")
+
+  (bandwidth-detection-interval
+   (maybe-integer 5)
+   "\
+How often in minutes interface specific detection of
+@var{max-bandwidth}
+is done for detecting possible changes when
+@var{bandwidth-detection}
+is enabled. Can be disabled by setting to @code{0}. Value range: @samp{0}..@samp{30}")
+
+  (boot-variation
+   (maybe-integer 15)
+   "\
+Time in seconds how much the boot time reported by system kernel can variate
+between updates. Value range: @samp{0}..@samp{300}")
+
+  (check-disk-space?
+   (maybe-boolean #t)
+   "\
+Enable or disable the availability check of at least some free disk space before
+a database write.")
+
+  (create-dirs?
+   (maybe-boolean #t)
+   "\
+Enable or disable the creation of directories when a configured path doesn't
+exist. This includes @var{database-dir}.")
+
+  ;; Note: Documentation for daemon-group and daemon-user adapted
+  ;; for user-group and user-account record-types.
+  (daemon-group
+   (maybe-user-group %vnstat-group)
+   "\
+Specify the group to which the daemon process should switch during startup.
+Set to @code{%unset-value} to disable group switching.")
+
+  (daemon-user
+   (maybe-user-account %vnstat-user)
+   "\
+Specify the user to which the daemon process should switch during startup.
+Set to @code{%unset-value} to disable user switching.")
+
+  (daily-days
+   (maybe-integer 62)
+   "\
+Data retention duration for the one day resolution entries. The configuration
+defines for how many past days entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (database-synchronous
+   (maybe-integer -1)
+   "\
+Change the setting of the SQLite \"synchronous\" flag which controls how much
+care is taken to ensure disk writes have fully completed when writing data to
+the database before continuing other actions. Higher values take extra steps
+to ensure data safety at the cost of slower performance. A value of @code{0} will
+result in all handling being left to the filesystem itself. Set to @code{-1} to
+select the default value according to database mode controlled by
+@var{database-write-ahead-logging}
+setting. See SQLite documentation for more details regarding values from @code{1}
+to @code{3}. Value range: @samp{-1}..@samp{3}")
+
+  (database-write-ahead-logging?
+   (maybe-boolean #f)
+   "\
+Enable or disable SQLite Write-Ahead Logging mode for the database. See SQLite
+documentation for more details and note that support for read-only operations
+isn't available in older SQLite versions.")
+
+  (hourly-days
+   (maybe-integer 4)
+   "\
+Data retention duration for the one hour resolution entries. The configuration
+defines for how many past days entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (log-file
+   maybe-string
+   "\
+Specify log file path and name to be used if @var{use-logging} is set to @code{1}.")
+
+  (max-bandwidth
+   maybe-integer
+   "\
+Maximum bandwidth for all interfaces. If the interface specific traffic
+exceeds the given value then the data is assumed to be invalid and rejected.
+Set to 0 in order to disable the feature. Value range: @samp{0}..@samp{50000}")
+
+  ;; documentation adapted for alist type
+  (max-bw
+   maybe-alist
+   "\
+Same as
+@var{max-bandwidth}
+but can be used for setting individual limits
+for selected interfaces. This is an association list of interfaces
+as symbols/strings to integer values. For example,
+@lisp
+(max-bw
+ `((eth0 . 15000)
+   (ppp0 . 10000)))
+@end lisp
+@var{bandwidth-detection}
+is disabled on an interface specific level for each
+@var{max-bw}
+configuration. Value range: @samp{0}..@samp{50000}"
+   (serializer
+    (lambda (field-name value)
+      (if (maybe-value-set? value)
+          (vnstat-serialize-alist field-name value) ""))))
+
+  (monthly-months
+   (maybe-integer 25)
+   "\
+Data retention duration for the one month resolution entries. The configuration
+defines for how many past months entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (month-rotate
+   (maybe-integer 1)
+   "\
+Day of month that months are expected to change. Usually set to
+1 but can be set to alternative values for example for tracking
+monthly billed traffic where the billing period doesn't start on
+the first day. For example, if set to 7, days of February up to and
+including the 6th will count for January. Changing this option will
+not cause existing data to be recalculated. Value range: @samp{1}..@samp{28}")
+
+  (month-rotate-affects-years?
+   (maybe-boolean #f)
+   "\
+Enable or disable
+@var{month-rotate}
+also affecting yearly data. Applicable only when
+@var{month-rotate}
+has a value greater than one.")
+
+  (offline-save-interval
+   (maybe-integer 30)
+   "\
+How often in minutes cached interface data is saved to file when all monitored
+interfaces are offline. Value range:
+@var{save-interval}..@samp{60}")
+
+  (pid-file
+   (maybe-string "/var/run/vnstat/vnstatd.pid")
+   "\
+Specify pid file path and name to be used.")
+
+  (poll-interval
+   (maybe-integer 5)
+   "\
+How often in seconds interfaces are checked for status changes.
+Value range: @samp{2}..@samp{60}")
+
+  (rescan-database-on-save?
+   maybe-boolean
+   "\
+Automatically discover added interfaces from the database and start monitoring.
+The rescan is done every
+@var{save-interval}
+or
+@var{offline-save-interval}
+minutes depending on the current activity state.")
+
+  (save-interval
+   (maybe-integer 5)
+   "\
+How often in minutes cached interface data is saved to file.
+Value range: (
+@var{update-interval} / 60 )..@samp{60}")
+
+  (save-on-status-change?
+   (maybe-boolean #t)
+   "\
+Enable or disable the additional saving to file of cached interface data
+when the availability of an interface changes, i.e., when an interface goes
+offline or comes online.")
+
+  (time-sync-wait
+   (maybe-integer 5)
+   "\
+How many minutes to wait during daemon startup for system clock to sync if
+most recent database update appears to be in the future. This may be needed
+in systems without a real-time clock (RTC) which require some time after boot
+to query and set the correct time. @code{0} = wait disabled.
+Value range: @samp{0}..@samp{60}")
+
+  (top-day-entries
+   (maybe-integer 20)
+   "\
+Data retention duration for the top day entries. The configuration
+defines how many of the past top day entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (trafficless-entries?
+   (maybe-boolean #t)
+   "\
+Create database entries even when there is no traffic during the entry's time
+period.")
+
+  (update-file-owner?
+   (maybe-boolean #t)
+   "\
+Enable or disable the update of file ownership during daemon process startup.
+During daemon startup, only database, log and pid files will be modified if the
+user or group change feature (
+@var{daemon-user}
+or
+@var{daemon-group}
+) is enabled and the files don't match the requested user or group. During manual
+database creation, this option will cause file ownership to be inherited from the
+database directory if the directory already exists. This option only has effect
+when the process is started as root or via sudo.")
+
+  (update-interval
+   (maybe-integer 20)
+   "\
+How often in seconds the interface data is updated. Value range:
+@var{poll-interval}..@samp{300}")
+
+  (use-logging
+   (maybe-integer 2)
+   "\
+Enable or disable logging. This option is ignored when the daemon is started with
+.B \"-n, --nodaemon\"
+which results in all log output being shown in terminal the daemon process is using.
+@code{0} = disabled, @code{1} = logfile and @code{2} = syslog.")
+
+  (use-utc?
+   maybe-boolean
+   "\
+Enable or disable using UTC as timezone in the database for all entries. When
+enabled, all entries added to the database will use UTC regardless of the
+configured system timezone. When disabled, the configured system timezone
+will be used. Changing this setting will not result in already existing
+data to be modified."
+   (serializer
+    (lambda (_ value)
+      (if (maybe-value-set? value)
+          (vnstat-serialize-boolean 'use-UTC value) ""))))
+
+  (yearly-years
+   (maybe-integer -1)
+   "\
+Data retention duration for the one year resolution entries. The configuration
+defines for how many past years entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (prefix vnstat-))
+
+(define (vnstat-serialize-configuration config)
+  (mixed-text-file
+   "vnstat.conf"
+   (serialize-configuration config vnstat-configuration-fields)))
+
+(define (vnstat-shepherd-service config)
+  (let ((config-file (vnstat-serialize-configuration config)))
+    (match-record config <vnstat-configuration> (package pid-file)
+      (shepherd-service
+       (documentation "Run vnstatd.")
+       (requirement `(networking file-systems))
+       (provision '(vnstatd))
+       (start #~(make-forkexec-constructor
+                 (list #$(file-append package "/sbin/vnstatd")
+                       "--daemon"
+                       "--config" #$config-file)
+                 #:pid-file #$pid-file))
+       (stop #~(make-kill-destructor))
+       (actions
+        (list (shepherd-configuration-action config-file)
+              (shepherd-action
+               (name 'reload)
+               (documentation "Reload vnstatd.")
+               (procedure
+                #~(lambda (pid)
+                    (if pid
+                        (begin
+                          (kill pid SIGHUP)
+                          (format #t
+                                  "Issued SIGHUP to vnstatd (PID ~a)."
+                                  pid))
+                        (format #t "vnstatd is not running.")))))))))))
+
+(define (vnstat-account-service config)
+  (match-record config <vnstat-configuration> (daemon-group daemon-user)
+    (filter-map maybe-value (list daemon-group daemon-user))))
+
+(define vnstat-service-type
+  (service-type
+   (name 'vnstat)
+   (description "vnStat network-traffic monitor service.")
+   (extensions
+    (list (service-extension shepherd-root-service-type
+                             (compose list vnstat-shepherd-service))
+          (service-extension account-service-type
+                             vnstat-account-service)))
+   (default-value (vnstat-configuration))))
+
 \f
 ;;;
 ;;; Zabbix server

base-commit: b9c9c23939a40a850a8c78579adaec25d1972bd1
-- 
2.39.2





^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH] services: Add vnstat-service-type.
  2023-04-04 13:08 ` [bug#60788] [PATCH v9] " Bruno Victal
@ 2023-04-07 15:22   ` Ludovic Courtès
  2023-04-07 20:04     ` Maxim Cournoyer
  2023-04-08 12:40     ` [bug#60788] [PATCH] services: Add vnstat-service-type Bruno Victal
  0 siblings, 2 replies; 27+ messages in thread
From: Ludovic Courtès @ 2023-04-07 15:22 UTC (permalink / raw)
  To: Bruno Victal; +Cc: maxim.cournoyer, 60788

Hi!

Bruno Victal <mirai@makinata.eu> skribis:

> * gnu/services/monitoring.scm (vnstat-service-type): New variable.
> * doc/guix.texi (Monitoring Services): Document it.
> ---
>
> Changes since v8:
> * Forgot to amend commit in v8, v9 corrects this.

Sorry to chime in after 9 versions (!).

I think a system test would be nice, we generally require it upfront,
but since Maxim wrote it can come later, let’s not let it block this
patch any longer.

One comment:

> +@item @code{database-dir} (default: @code{"/var/lib/vnstat"}) (type: string)

[...]

> +@item @code{create-dirs?} (default: @code{#t}) (type: maybe-boolean)

For consistency, both within this record and with the rest of Guix, I
suggest avoiding abbreviations.  Since this will be part of the API,
better fix it now than later.

> +@item @code{max-bandwidth} (type: maybe-integer)
> +Maximum bandwidth for all interfaces.  If the interface specific traffic
> +exceeds the given value then the data is assumed to be invalid and
> +rejected.  Set to 0 in order to disable the feature.  Value range:
> +@samp{0}..@samp{50000}
> +
> +@item @code{max-bw} (type: maybe-alist)
> +Same as @var{max-bandwidth} but can be used for setting individual
> +limits for selected interfaces.  This is an association list of
> +interfaces as symbols/strings to integer values.  For example,
> +@lisp
> +(max-bw `((eth0 .  15000)
> +          (ppp0 .  10000)))
> +@end lisp

Both the naming and semantics are a bit confusing to me.

How about s/max-bw/per-interface-max-bandwidth/ ?

Side note: I’d represent interfaces as strings because there’s no
guarantee they “fit” in a symbol.

That’s all, thanks!

Ludo’.




^ permalink raw reply	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH] services: Add vnstat-service-type.
  2023-04-07 15:22   ` [bug#60788] [PATCH] " Ludovic Courtès
@ 2023-04-07 20:04     ` Maxim Cournoyer
  2023-04-20 10:03       ` [bug#60788] Policy for system tests? Ludovic Courtès
  2023-04-08 12:40     ` [bug#60788] [PATCH] services: Add vnstat-service-type Bruno Victal
  1 sibling, 1 reply; 27+ messages in thread
From: Maxim Cournoyer @ 2023-04-07 20:04 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: Bruno Victal, 60788

Hi Ludo,

Ludovic Courtès <ludo@gnu.org> writes:

> Hi!
>
> Bruno Victal <mirai@makinata.eu> skribis:
>
>> * gnu/services/monitoring.scm (vnstat-service-type): New variable.
>> * doc/guix.texi (Monitoring Services): Document it.
>> ---
>>
>> Changes since v8:
>> * Forgot to amend commit in v8, v9 corrects this.
>
> Sorry to chime in after 9 versions (!).
>
> I think a system test would be nice, we generally require it upfront,
> but since Maxim wrote it can come later, let’s not let it block this
> patch any longer.

I didn't mean to lower our standards; I wasn't sure if that policy of
ours was strict, since a few system services do not have corresponding
tests, IIRC.  I wary a bit that demanding a system test for each added
service may cause scalability problems in the long run, as each demand a
disk-heavy image to be generated and the test to run in a VM, which
makes it expensive/slow.  On the other hand, it's nice to know about any
regressions when they happen rather than on a reboot...

If we have such a policy, perhaps we should explicit it in
our documented contribution guidelines?

-- 
Thanks,
Maxim




^ permalink raw reply	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH] services: Add vnstat-service-type.
  2023-04-07 15:22   ` [bug#60788] [PATCH] " Ludovic Courtès
  2023-04-07 20:04     ` Maxim Cournoyer
@ 2023-04-08 12:40     ` Bruno Victal
  2023-04-20 10:09       ` Ludovic Courtès
  1 sibling, 1 reply; 27+ messages in thread
From: Bruno Victal @ 2023-04-08 12:40 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: maxim.cournoyer, 60788

Hi  Ludo’,

On 2023-04-07 16:22, Ludovic Courtès wrote:
> I think a system test would be nice, we generally require it upfront,
> but since Maxim wrote it can come later, let’s not let it block this
> patch any longer.

Originally I thought it was unfeasible to write a test for this service
since it required network activity within the VM and it seemed to take more
than 10 minutes for it to pick it up.

I did some manual experiments on it much later and I managed to get it down to
approx. between 2 and 3 minutes for a partially automated test.

What's blocking the test from being implemented in Guix is that my (incomplete) test-suite
for this depends on guile 'spawn', which isn't available yet,
For reference, I've attached the test-suite here [1], the plan is to finish it up and
add it to Guix after the 'spawn' issue is resolved. (or perhaps refactor this to use another approach?)

> 
> One comment:
> 
>> +@item @code{database-dir} (default: @code{"/var/lib/vnstat"}) (type: string)
> 
> [...]
> 
>> +@item @code{create-dirs?} (default: @code{#t}) (type: maybe-boolean)
> 
> For consistency, both within this record and with the rest of Guix, I
> suggest avoiding abbreviations.  Since this will be part of the API,
> better fix it now than later.

I should mention that almost all of the field names here are near verbatim
vnstat config-file directives, i.e. a near 1-1 Scheme translation of vnstat config.
This has the benefit that it makes serialization pretty much straightforward.

It's possible to override their names by the use of the custom serializer parameter
but would it be acceptable to leave them as-is?

>> +@item @code{max-bandwidth} (type: maybe-integer)
>> +Maximum bandwidth for all interfaces.  If the interface specific traffic
>> +exceeds the given value then the data is assumed to be invalid and
>> +rejected.  Set to 0 in order to disable the feature.  Value range:
>> +@samp{0}..@samp{50000}
>> +
>> +@item @code{max-bw} (type: maybe-alist)
>> +Same as @var{max-bandwidth} but can be used for setting individual
>> +limits for selected interfaces.  This is an association list of
>> +interfaces as symbols/strings to integer values.  For example,
>> +@lisp
>> +(max-bw `((eth0 .  15000)
>> +          (ppp0 .  10000)))
>> +@end lisp
> 
> Both the naming and semantics are a bit confusing to me.
> 
> How about s/max-bw/per-interface-max-bandwidth/ ?

I found it a bit confusing as well but I'm not too familiar with this part of
the config to comment about it.
I lifted most of the field-names and documentations straight from the manpage.

> Side note: I’d represent interfaces as strings because there’s no
> guarantee they “fit” in a symbol.

Thanks! I'll have this amended in the next revision.


[1]: Listing of vnstat-test.scm

--8<---------------cut here---------------start------------->8---
(define-module (gnu tests vnstat)
  #:use-module (gnu tests)
  #:use-module ((gnu packages networking) #:select (socat vnstat))
  #:use-module (gnu services)
  #:use-module (gnu services networking)
  #:use-module (gnu services monitoring)
  #:use-module (gnu system)
  #:use-module (gnu system vm)
  #:use-module (guix gexp)
  #:use-module (ice-9 format)
  #:export (%test-vnstat))


(define (run-vnstat-test)
  "Run tests in a vm which has vnstat running."

  (define vnstat-config
    (vnstat-configuration
     (max-bandwidth 0)
     (time-sync-wait 0)
     (bandwidth-detection-interval 0)))

  (define os
    (marionette-operating-system
     (simple-operating-system
      (service dhcp-client-service-type)
      (service vnstat-service-type
               vnstat-config)
      (service inetd-service-type
               (inetd-configuration
                (entries
                 (list (inetd-entry
                        (name "discard")
                        (socket-type 'stream)
                        (protocol "tcp")   ;; FIXME: originally this was UDP but port-forwardings hardcodes TCP
                        (wait? #t)
                        (user "nobody")))))))
     #:imported-modules '((gnu services herd))))

  (define forwarded-port 9999)

  (define vm
    (virtual-machine
     (operating-system os)
     ;; Note: port 9 corresponds to "discard" service.
     (port-forwardings `((,forwarded-port . 9)))))   ;; FIXME: Allow UDP forward.

  (define test-timeout (* 60 2))  ; wait for 2 minutes tops.

  (define test
    (with-imported-modules '((gnu build marionette))
      #~(begin
          (use-modules (gnu build marionette)
                       (srfi srfi-64))

          (let ((marionette (make-marionette (list #$vm)))
                (pid-file #$(vnstat-configuration-pid-file vnstat-config)))

            (test-runner-current (system-test-runner #$output))
            (test-begin "vnstat")

            (test-assert "service is running"
              (marionette-eval
               '(begin
                  (use-modules (gnu services herd))
                  (start-service 'vnstatd))
               marionette))

            (test-assert "vnstatd ready"
              (wait-for-file pid-file marionette))

            (test-assert "vnstatd is logging"
              ;; pump garbage into the "discard" service within the vm
              ;; TODO: guile socket client instead? Is it feasible?
              (let* ((socat #$(file-append socat "/bin/socat"))
                     (dest-addr #$(format #f "TCP4:localhost:~d"
                                          forwarded-port))
                     (args `("socat" "-u" "/dev/zero" ,dest-addr))
                     ;; XXX: Guile bug (22/03/2023, Guile 3.0.9)
                     ;;      Fixed in main: <https://issues.guix.gnu.org/61073>
                     #;(output-port (%make-void-port "w"))
                     (garbage-pump-pid (spawn socat args)))

                (let ((retval
                       (marionette-eval
                        '(begin
                           (use-modules (ice-9 popen)
                                        ;(ice-9 rdelim)
                                        (ice-9 match)
                                        (sxml simple)
                                        (sxml xpath))

                           (define selector
                             (let ((xpath '(vnstat interface traffic total)))
                               (compose (node-pos 1) (sxpath xpath))))

                           (let loop ((i 0))
                             (let* ((vnstat #$(file-append vnstat "/bin/vnstat"))
                                    (query-cmd (format #f "~a --xml") vnstat)
                                    (result
                                     #;(call-with-port
                                     (open-input-pipe query-cmd) read-line)
                                     (call-with-port
                                         (open-input-pipe query-cmd) xml->sxml))
                                    (iface-stats (selector result)))
                               (match iface-stats
                                 ((('total ('rx "0") ('tx "0")))
                                  (sleep 1)
                                  (if (< i #$test-timeout)
                                      (loop (+ i 1))
                                      #f))
                                 ((('total ('rx rx) ('tx tx)))
                                  #t)
                                 (_ #f)))))
                        marionette)))
                  ;; shutdown garbage pump
                  (kill garbage-pump-pid SIGTERM)
                  retval)))

            (test-end)))))

  (gexp->derivation "vnstat-test" test))

(define %test-vnstat
  (system-test
   (name "vnstat")
   (description "Basic tests for vnstat service.")
   (value (run-vnstat-test))))
--8<---------------cut here---------------end--------------->8---


Cheers,
Bruno




^ permalink raw reply	[flat|nested] 27+ messages in thread

* [bug#60788] Policy for system tests?
  2023-04-07 20:04     ` Maxim Cournoyer
@ 2023-04-20 10:03       ` Ludovic Courtès
  0 siblings, 0 replies; 27+ messages in thread
From: Ludovic Courtès @ 2023-04-20 10:03 UTC (permalink / raw)
  To: Maxim Cournoyer; +Cc: Bruno Victal, 60788

Hi,

Maxim Cournoyer <maxim.cournoyer@gmail.com> skribis:

> Ludovic Courtès <ludo@gnu.org> writes:

[...]

>> I think a system test would be nice, we generally require it upfront,
>> but since Maxim wrote it can come later, let’s not let it block this
>> patch any longer.
>
> I didn't mean to lower our standards; I wasn't sure if that policy of
> ours was strict, since a few system services do not have corresponding
> tests, IIRC.  I wary a bit that demanding a system test for each added
> service may cause scalability problems in the long run, as each demand a
> disk-heavy image to be generated and the test to run in a VM, which
> makes it expensive/slow.  On the other hand, it's nice to know about any
> regressions when they happen rather than on a reboot...

Yeah, it’s an unwritten policy; I think we’ve consistently required it
for some time now.  It’s useful because otherwise it’s hard to tell what
the status is for a service.

(Speaking of which, we do *not* have that policy for Home services,
because we don’t even have a test strategy, and that’s something we
should fix before it’s too late.)

> If we have such a policy, perhaps we should explicit it in
> our documented contribution guidelines?

Yes!

Also, we should split the submission guidelines into different
categories: packages, services, doc, core, etc.

Ludo’.




^ permalink raw reply	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH] services: Add vnstat-service-type.
  2023-04-08 12:40     ` [bug#60788] [PATCH] services: Add vnstat-service-type Bruno Victal
@ 2023-04-20 10:09       ` Ludovic Courtès
  0 siblings, 0 replies; 27+ messages in thread
From: Ludovic Courtès @ 2023-04-20 10:09 UTC (permalink / raw)
  To: Bruno Victal; +Cc: maxim.cournoyer, 60788

Hello!

Bruno Victal <mirai@makinata.eu> skribis:

>>> +@item @code{database-dir} (default: @code{"/var/lib/vnstat"}) (type: string)
>> 
>> [...]
>> 
>>> +@item @code{create-dirs?} (default: @code{#t}) (type: maybe-boolean)
>> 
>> For consistency, both within this record and with the rest of Guix, I
>> suggest avoiding abbreviations.  Since this will be part of the API,
>> better fix it now than later.
>
> I should mention that almost all of the field names here are near verbatim
> vnstat config-file directives, i.e. a near 1-1 Scheme translation of vnstat config.
> This has the benefit that it makes serialization pretty much straightforward.
>
> It's possible to override their names by the use of the custom serializer parameter
> but would it be acceptable to leave them as-is?

Hmm, I’d say that if the cost of using “nice names” is “really high”,
then yes.  But perhaps we can make that cost low by having a map for the
few cases where we use a name different from upstream?

  (define field-name-mapping
    '((database-directory . "database_dir") …))

> (define-module (gnu tests vnstat)

Woohoo, you rock!

>             (test-assert "vnstatd is logging"
>               ;; pump garbage into the "discard" service within the vm
>               ;; TODO: guile socket client instead? Is it feasible?
>               (let* ((socat #$(file-append socat "/bin/socat"))
>                      (dest-addr #$(format #f "TCP4:localhost:~d"
>                                           forwarded-port))
>                      (args `("socat" "-u" "/dev/zero" ,dest-addr))
>                      ;; XXX: Guile bug (22/03/2023, Guile 3.0.9)
>                      ;;      Fixed in main: <https://issues.guix.gnu.org/61073>
>                      #;(output-port (%make-void-port "w"))
>                      (garbage-pump-pid (spawn socat args)))

You can probably connect directly to DEST-ADDR from Guile instead of
going through ‘socat’?

If not, you can either pass ‘#:guile guile-3.0-latest’ to
‘gexp->derivation’ so you get ‘spawn’ (3.0.9 is the default in
‘core-updates’ anyway), or use ‘primitive-fork’.

HTH!

Ludo’.




^ permalink raw reply	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH v10 1/3] services: Add vnstat-service-type.
  2023-01-13 20:07 [bug#60788] [PATCH] services: Add vnstat-service-type Bruno Victal
                   ` (10 preceding siblings ...)
  2023-04-04 13:08 ` [bug#60788] [PATCH v9] " Bruno Victal
@ 2023-05-05  0:18 ` Bruno Victal
  2023-05-11 14:33   ` bug#60788: " Ludovic Courtès
  2023-05-05  0:18 ` [bug#60788] [PATCH v10 2/3] services: inetd: Export accessors Bruno Victal
  2023-05-05  0:18 ` [bug#60788] [PATCH v10 3/3] tests: Add vnstat tests Bruno Victal
  13 siblings, 1 reply; 27+ messages in thread
From: Bruno Victal @ 2023-05-05  0:18 UTC (permalink / raw)
  To: 60788; +Cc: ludo, Bruno Victal, maxim.cournoyer

* gnu/services/monitoring.scm (vnstat-service-type): New variable.
* doc/guix.texi (Monitoring Services): Document it.
---

Notable changes since v9:
* Fields create-dirs? and database-dir were renamed to create-directories? and database-directory.
* Field maxbw description asks that interface names be supplied as strings instead.
* Test suite was refactored and included in this series.
* Exports accessors for inetd service.

Notes:
Regarding the MaxBW field, I don't understand this option enough to decide on a different naming for it.

 doc/guix.texi               | 237 ++++++++++++++++++
 gnu/services/monitoring.scm | 472 ++++++++++++++++++++++++++++++++++++
 2 files changed, 709 insertions(+)

diff --git a/doc/guix.texi b/doc/guix.texi
index 55221a10c3..bedc38cb73 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -28656,6 +28656,243 @@ Monitoring Services
 @end table
 @end deftp
 
+@anchor{vnstat}
+@subsubheading vnStat Network Traffic Monitor
+@cindex vnstat
+
+vnStat is a network traffic monitor that uses interface statistics provided
+by the kernel rather than traffic sniffing.  This makes it a light resource
+monitor, regardless of network traffic rate.
+
+@defvar vnstat-service-type
+This is the service type for the @uref{https://humdi.net/vnstat/,vnStat} daemon
+and accepts a @code{vnstat-configuration} value.
+
+The following example will configure the service with default values:
+
+@lisp
+(service vnstat-service-type)
+@end lisp
+@end defvar
+
+@c %start of fragment
+@deftp {Data Type} vnstat-configuration
+Available @code{vnstat-configuration} fields are:
+
+@table @asis
+@item @code{package} (default: @code{vnstat}) (type: file-like)
+The vnstat package.
+
+@item @code{database-directory} (default: @code{"/var/lib/vnstat"}) (type: string)
+Specifies the directory where the database is to be stored.  A full path
+must be given and a leading '/' isn't required.
+
+@item @code{5-minute-hours} (default: @code{48}) (type: maybe-integer)
+Data retention duration for the 5 minute resolution entries.  The
+configuration defines for how many past hours entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@item @code{64bit-interface-counters} (default: @code{-2}) (type: maybe-integer)
+Select interface counter handling.  Set to @code{1} for defining that
+all interfaces use 64-bit counters on the kernel side and @code{0} for
+defining 32-bit counter.  Set to @code{-1} for using the old style logic
+used in earlier versions where counter values within 32-bits are assumed
+to be 32-bit and anything larger is assumed to be a 64-bit counter.  This
+may produce false results if a 64-bit counter is reset within the
+32-bits.  Set to @code{-2} for using automatic detection based on
+available kernel datastructures.
+
+@item @code{always-add-new-interfaces?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable automatic creation of new database entries for
+interfaces not currently in the database even if the database file
+already exists when the daemon is started.  New database entries will
+also get created for new interfaces seen while the daemon is running.
+Pseudo interfaces @samp{lo}, @samp{lo0} and @samp{sit0} are always excluded from getting
+added.
+
+@item @code{bandwidth-detection?} (default: @code{#t}) (type: maybe-boolean)
+Try to automatically detect @var{max-bandwidth} value for each monitored
+interface.  Mostly only ethernet interfaces support this feature.
+@var{max-bandwidth} will be used as fallback value if detection fails.
+Any interface specific @var{max-BW} configuration will disable the
+detection for the specified interface.  In Linux, the detection is
+disabled for tun interfaces due to the Linux kernel always reporting 10
+Mbit regardless of the used real interface.
+
+@item @code{bandwidth-detection-interval} (default: @code{5}) (type: maybe-integer)
+How often in minutes interface specific detection of @var{max-bandwidth}
+is done for detecting possible changes when @var{bandwidth-detection} is
+enabled.  Can be disabled by setting to @code{0}.  Value range:
+@samp{0}..@samp{30}
+
+@item @code{boot-variation} (default: @code{15}) (type: maybe-integer)
+Time in seconds how much the boot time reported by system kernel can
+variate between updates.  Value range: @samp{0}..@samp{300}
+
+@item @code{check-disk-space?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the availability check of at least some free disk
+space before a database write.
+
+@item @code{create-directories?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the creation of directories when a configured path
+doesn't exist.  This includes @var{database-directory}.
+
+@item @code{daemon-group} (type: maybe-user-group)
+Specify the group to which the daemon process should switch during
+startup.  Set to @code{%unset-value} to disable group switching.
+
+@item @code{daemon-user} (type: maybe-user-account)
+Specify the user to which the daemon process should switch during
+startup.  Set to @code{%unset-value} to disable user switching.
+
+@item @code{daily-days} (default: @code{62}) (type: maybe-integer)
+Data retention duration for the one day resolution entries.  The
+configuration defines for how many past days entries will be stored.  Set
+to @code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{database-synchronous} (default: @code{-1}) (type: maybe-integer)
+Change the setting of the SQLite "synchronous" flag which controls how
+much care is taken to ensure disk writes have fully completed when
+writing data to the database before continuing other actions.  Higher
+values take extra steps to ensure data safety at the cost of slower
+performance.  A value of @code{0} will result in all handling being left
+to the filesystem itself.  Set to @code{-1} to select the default value
+according to database mode controlled by
+@var{database-write-ahead-logging} setting.  See SQLite documentation
+for more details regarding values from @code{1} to @code{3}.  Value
+range: @samp{-1}..@samp{3}
+
+@item @code{database-write-ahead-logging?} (default: @code{#f}) (type: maybe-boolean)
+Enable or disable SQLite Write-Ahead Logging mode for the database.  See
+SQLite documentation for more details and note that support for
+read-only operations isn't available in older SQLite versions.
+
+@item @code{hourly-days} (default: @code{4}) (type: maybe-integer)
+Data retention duration for the one hour resolution entries.  The
+configuration defines for how many past days entries will be stored.  Set
+to @code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{log-file} (type: maybe-string)
+Specify log file path and name to be used if @var{use-logging} is set to
+@code{1}.
+
+@item @code{max-bandwidth} (type: maybe-integer)
+Maximum bandwidth for all interfaces.  If the interface specific traffic
+exceeds the given value then the data is assumed to be invalid and
+rejected.  Set to 0 in order to disable the feature.  Value range:
+@samp{0}..@samp{50000}
+
+@item @code{max-bw} (type: maybe-alist)
+Same as @var{max-bandwidth} but can be used for setting individual
+limits for selected interfaces.  This is an association list of
+interfaces as strings to integer values.  For example,
+@lisp
+(max-bw `(("eth0" .  15000)
+          ("ppp0" .  10000)))
+@end lisp
+@var{bandwidth-detection} is disabled on an interface specific level for
+each @var{max-bw} configuration.  Value range: @samp{0}..@samp{50000}
+
+@item @code{monthly-months} (default: @code{25}) (type: maybe-integer)
+Data retention duration for the one month resolution entries.  The
+configuration defines for how many past months entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@item @code{month-rotate} (default: @code{1}) (type: maybe-integer)
+Day of month that months are expected to change.  Usually set to 1 but
+can be set to alternative values for example for tracking monthly billed
+traffic where the billing period doesn't start on the first day.  For
+example, if set to 7, days of February up to and including the 6th will
+count for January.  Changing this option will not cause existing data to
+be recalculated.  Value range: @samp{1}..@samp{28}
+
+@item @code{month-rotate-affects-years?} (default: @code{#f}) (type: maybe-boolean)
+Enable or disable @var{month-rotate} also affecting yearly data.
+Applicable only when @var{month-rotate} has a value greater than one.
+
+@item @code{offline-save-interval} (default: @code{30}) (type: maybe-integer)
+How often in minutes cached interface data is saved to file when all
+monitored interfaces are offline.  Value range:
+@var{save-interval}..@samp{60}
+
+@item @code{pid-file} (default: @code{"/var/run/vnstatd.pid"}) (type: maybe-string)
+Specify pid file path and name to be used.
+
+@item @code{poll-interval} (default: @code{5}) (type: maybe-integer)
+How often in seconds interfaces are checked for status changes.  Value
+range: @samp{2}..@samp{60}
+
+@item @code{rescan-database-on-save?} (type: maybe-boolean)
+Automatically discover added interfaces from the database and start
+monitoring.  The rescan is done every @var{save-interval} or
+@var{offline-save-interval} minutes depending on the current activity
+state.
+
+@item @code{save-interval} (default: @code{5}) (type: maybe-integer)
+How often in minutes cached interface data is saved to file.  Value
+range: ( @var{update-interval} / 60 )..@samp{60}
+
+@item @code{save-on-status-change?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the additional saving to file of cached interface data
+when the availability of an interface changes, i.e., when an interface
+goes offline or comes online.
+
+@item @code{time-sync-wait} (default: @code{5}) (type: maybe-integer)
+How many minutes to wait during daemon startup for system clock to sync
+if most recent database update appears to be in the future.  This may be
+needed in systems without a real-time clock (RTC) which require some
+time after boot to query and set the correct time.  @code{0} = wait
+disabled.  Value range: @samp{0}..@samp{60}
+
+@item @code{top-day-entries} (default: @code{20}) (type: maybe-integer)
+Data retention duration for the top day entries.  The configuration
+defines how many of the past top day entries will be stored.  Set to
+@code{-1} for unlimited entries or to @code{0} to disable the data
+collection of this resolution.
+
+@item @code{trafficless-entries?} (default: @code{#t}) (type: maybe-boolean)
+Create database entries even when there is no traffic during the entry's
+time period.
+
+@item @code{update-file-owner?} (default: @code{#t}) (type: maybe-boolean)
+Enable or disable the update of file ownership during daemon process
+startup.  During daemon startup, only database, log and pid files will
+be modified if the user or group change feature ( @var{daemon-user} or
+@var{daemon-group} ) is enabled and the files don't match the requested
+user or group.  During manual database creation, this option will cause
+file ownership to be inherited from the database directory if the
+directory already exists.  This option only has effect when the process
+is started as root or via sudo.
+
+@item @code{update-interval} (default: @code{20}) (type: maybe-integer)
+How often in seconds the interface data is updated.  Value range:
+@var{poll-interval}..@samp{300}
+
+@item @code{use-logging} (default: @code{2}) (type: maybe-integer)
+Enable or disable logging.  Accepted values are: @code{0} = disabled,
+@code{1} = logfile and @code{2} = syslog.
+
+@item @code{use-utc?} (type: maybe-boolean)
+Enable or disable using UTC as timezone in the database for all entries.
+When enabled, all entries added to the database will use UTC regardless
+of the configured system timezone.  When disabled, the configured system
+timezone will be used.  Changing this setting will not result in already
+existing data to be modified.
+
+@item @code{yearly-years} (default: @code{-1}) (type: maybe-integer)
+Data retention duration for the one year resolution entries.  The
+configuration defines for how many past years entries will be stored.
+Set to @code{-1} for unlimited entries or to @code{0} to disable the
+data collection of this resolution.
+
+@end table
+@end deftp
+@c %end of fragment
+
 @subsubheading Zabbix server
 @cindex zabbix zabbix-server
 Zabbix is a high performance monitoring system that can collect data from a
diff --git a/gnu/services/monitoring.scm b/gnu/services/monitoring.scm
index bbf8b10f8b..e698040078 100644
--- a/gnu/services/monitoring.scm
+++ b/gnu/services/monitoring.scm
@@ -3,6 +3,7 @@
 ;;; Copyright © 2018, 2019 Gábor Boskovits <boskovits@gmail.com>
 ;;; Copyright © 2018, 2019, 2020 Oleg Pykhalov <go.wigust@gmail.com>
 ;;; Copyright © 2022 Marius Bakke <marius@gnu.org>
+;;; Copyright © 2023 Bruno Victal <mirai@makinata.eu>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -26,6 +27,7 @@ (define-module (gnu services monitoring)
   #:use-module (gnu services web)
   #:use-module (gnu packages admin)
   #:use-module (gnu packages monitoring)
+  #:use-module (gnu packages networking)
   #:use-module (gnu system shadow)
   #:use-module (guix gexp)
   #:use-module (guix packages)
@@ -34,6 +36,7 @@ (define-module (gnu services monitoring)
   #:use-module ((guix ui) #:select (display-hint G_))
   #:use-module (ice-9 match)
   #:use-module (ice-9 rdelim)
+  #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-26)
   #:use-module (srfi srfi-35)
   #:export (darkstat-configuration
@@ -45,6 +48,46 @@ (define-module (gnu services monitoring)
             prometheus-node-exporter-web-listen-address
             prometheus-node-exporter-service-type
 
+            vnstat-configuration
+            vnstat-configuration?
+            vnstat-service-type
+            vnstat-configuration-package
+            vnstat-configuration-database-directory
+            vnstat-configuration-5-minute-hours
+            vnstat-configuration-64bit-interface-counters
+            vnstat-configuration-always-add-new-interfaces?
+            vnstat-configuration-bandwidth-detection?
+            vnstat-configuration-bandwidth-detection-interval
+            vnstat-configuration-boot-variation
+            vnstat-configuration-check-disk-space?
+            vnstat-configuration-create-directories?
+            vnstat-configuration-daemon-group
+            vnstat-configuration-daemon-user
+            vnstat-configuration-daily-days
+            vnstat-configuration-database-synchronous
+            vnstat-configuration-database-write-ahead-logging?
+            vnstat-configuration-hourly-days
+            vnstat-configuration-log-file
+            vnstat-configuration-max-bandwidth
+            vnstat-configuration-max-bw
+            vnstat-configuration-monthly-months
+            vnstat-configuration-month-rotate
+            vnstat-configuration-month-rotate-affects-years?
+            vnstat-configuration-offline-save-interval
+            vnstat-configuration-pid-file
+            vnstat-configuration-poll-interval
+            vnstat-configuration-rescan-database-on-save?
+            vnstat-configuration-save-interval
+            vnstat-configuration-save-on-status-change?
+            vnstat-configuration-time-sync-wait
+            vnstat-configuration-top-day-entries
+            vnstat-configuration-trafficless-entries?
+            vnstat-configuration-update-file-owner?
+            vnstat-configuration-update-interval
+            vnstat-configuration-use-logging
+            vnstat-configuration-use-utc?
+            vnstat-configuration-yearly-years
+
             zabbix-server-configuration
             zabbix-server-service-type
             zabbix-agent-configuration
@@ -196,6 +239,435 @@ (define prometheus-node-exporter-service-type
                         prometheus-node-exporter-shepherd-service)))
    (default-value (prometheus-node-exporter-configuration))))
 
+\f
+;;;
+;;; vnstat daemon
+;;;
+
+(define* (camelfy-field-name field-name #:key (dromedary? #f))
+  (match (string-split (symbol->string field-name) #\-)
+    ((head tail ...)
+     (string-join (cons (if dromedary? head (string-upcase head 0 1))
+                        (map (cut string-upcase <> 0 1) tail)) ""))))
+
+(define (strip-trailing-?-character field-name)
+  "Drop rightmost '?' character"
+  (let ((str (symbol->string field-name)))
+    (if (string-suffix? "?" str)
+        (string->symbol (string-drop-right str 1))
+        field-name)))
+
+(define (vnstat-serialize-string field-name value)
+  #~(format #f "~a ~s~%"
+            #$(camelfy-field-name field-name)
+            #$value))
+
+(define vnstat-serialize-integer vnstat-serialize-string)
+
+(define (vnstat-serialize-boolean field-name value)
+  #~(format #f "~a ~a~%"
+            #$(camelfy-field-name (strip-trailing-?-character field-name))
+            #$(if value 1 0)))
+
+(define (vnstat-serialize-alist field-name value)
+  (generic-serialize-alist string-append
+                           (lambda (iface val)
+                             (vnstat-serialize-integer
+                              (format #f "MaxBW~a" iface) val))
+                           value))
+
+(define (vnstat-serialize-user-account field-name value)
+  (vnstat-serialize-string field-name (user-account-name value)))
+
+(define (vnstat-serialize-user-group field-name value)
+  (vnstat-serialize-string field-name (user-group-name value)))
+
+(define-maybe string  (prefix vnstat-))
+(define-maybe integer (prefix vnstat-))
+(define-maybe boolean (prefix vnstat-))
+(define-maybe alist   (prefix vnstat-))
+(define-maybe user-account (prefix vnstat-))
+(define-maybe user-group (prefix vnstat-))
+
+(define %vnstat-user
+  (user-account
+   (name "vnstat")
+   (group "vnstat")
+   (system? #t)
+   (home-directory "/var/empty")
+   (shell (file-append shadow "/sbin/nologin"))))
+
+(define %vnstat-group
+  (user-group
+   (name "vnstat")
+   (system? #t)))
+
+;; Documentation strings from vnstat.conf manpage adapted to texinfo.
+;; vnstat checkout: v2.10, commit b3408af1c609aa6265d296cab7bfe59a61d7cf70
+;; Do not reflow these strings or drop the initial \ escape as it makes it
+;; harder to diff against the manpage.
+(define-configuration vnstat-configuration
+  (package
+    (file-like vnstat)
+    "The vnstat package."
+    empty-serializer)
+
+  (database-directory
+   (string "/var/lib/vnstat")
+   "\
+Specifies the directory where the database is to be stored.
+A full path must be given and a leading '/' isn't required."
+   (serializer
+    (lambda (_ value)
+      (vnstat-serialize-string 'database-dir value))))
+
+  (5-minute-hours
+   (maybe-integer 48)
+   "\
+Data retention duration for the 5 minute resolution entries. The configuration
+defines for how many past hours entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (64bit-interface-counters
+   (maybe-integer -2)
+   "\
+Select interface counter handling. Set to @code{1} for defining that all interfaces
+use 64-bit counters on the kernel side and @code{0} for defining 32-bit counter. Set
+to @code{-1} for using the old style logic used in earlier versions where counter
+values within 32-bits are assumed to be 32-bit and anything larger is assumed to
+be a 64-bit counter. This may produce false results if a 64-bit counter is
+reset within the 32-bits. Set to @code{-2} for using automatic detection based on
+available kernel datastructures.")
+
+  (always-add-new-interfaces?
+   (maybe-boolean #t)
+   "\
+Enable or disable automatic creation of new database entries for interfaces not
+currently in the database even if the database file already exists when the
+daemon is started. New database entries will also get created for new interfaces
+seen while the daemon is running. Pseudo interfaces @samp{lo}, @samp{lo0} and @samp{sit0} are always
+excluded from getting added.")
+
+  (bandwidth-detection?
+   (maybe-boolean #t)
+   "\
+Try to automatically detect
+@var{max-bandwidth}
+value for each monitored interface. Mostly only ethernet interfaces support
+this feature.
+@var{max-bandwidth}
+will be used as fallback value if detection fails. Any interface specific
+@var{max-BW}
+configuration will disable the detection for the specified interface.
+In Linux, the detection is disabled for tun interfaces due to the
+Linux kernel always reporting 10 Mbit regardless of the used real interface.")
+
+  (bandwidth-detection-interval
+   (maybe-integer 5)
+   "\
+How often in minutes interface specific detection of
+@var{max-bandwidth}
+is done for detecting possible changes when
+@var{bandwidth-detection}
+is enabled. Can be disabled by setting to @code{0}. Value range: @samp{0}..@samp{30}")
+
+  (boot-variation
+   (maybe-integer 15)
+   "\
+Time in seconds how much the boot time reported by system kernel can variate
+between updates. Value range: @samp{0}..@samp{300}")
+
+  (check-disk-space?
+   (maybe-boolean #t)
+   "\
+Enable or disable the availability check of at least some free disk space before
+a database write.")
+
+  (create-directories?
+   (maybe-boolean #t)
+   "\
+Enable or disable the creation of directories when a configured path doesn't
+exist. This includes @var{database-directory}."
+   (serializer
+    (lambda (_ value)
+      (if (maybe-value-set? value)
+          (vnstat-serialize-boolean 'create-dirs value) ""))))
+
+  ;; Note: Documentation for daemon-group and daemon-user adapted
+  ;; for user-group and user-account record-types.
+  (daemon-group
+   (maybe-user-group %vnstat-group)
+   "\
+Specify the group to which the daemon process should switch during startup.
+Set to @code{%unset-value} to disable group switching.")
+
+  (daemon-user
+   (maybe-user-account %vnstat-user)
+   "\
+Specify the user to which the daemon process should switch during startup.
+Set to @code{%unset-value} to disable user switching.")
+
+  (daily-days
+   (maybe-integer 62)
+   "\
+Data retention duration for the one day resolution entries. The configuration
+defines for how many past days entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (database-synchronous
+   (maybe-integer -1)
+   "\
+Change the setting of the SQLite \"synchronous\" flag which controls how much
+care is taken to ensure disk writes have fully completed when writing data to
+the database before continuing other actions. Higher values take extra steps
+to ensure data safety at the cost of slower performance. A value of @code{0} will
+result in all handling being left to the filesystem itself. Set to @code{-1} to
+select the default value according to database mode controlled by
+@var{database-write-ahead-logging}
+setting. See SQLite documentation for more details regarding values from @code{1}
+to @code{3}. Value range: @samp{-1}..@samp{3}")
+
+  (database-write-ahead-logging?
+   (maybe-boolean #f)
+   "\
+Enable or disable SQLite Write-Ahead Logging mode for the database. See SQLite
+documentation for more details and note that support for read-only operations
+isn't available in older SQLite versions.")
+
+  (hourly-days
+   (maybe-integer 4)
+   "\
+Data retention duration for the one hour resolution entries. The configuration
+defines for how many past days entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (log-file
+   maybe-string
+   "\
+Specify log file path and name to be used if @var{use-logging} is set to @code{1}.")
+
+  (max-bandwidth
+   maybe-integer
+   "\
+Maximum bandwidth for all interfaces. If the interface specific traffic
+exceeds the given value then the data is assumed to be invalid and rejected.
+Set to 0 in order to disable the feature. Value range: @samp{0}..@samp{50000}")
+
+  ;; documentation adapted for alist type
+  (max-bw
+   maybe-alist
+   "\
+Same as
+@var{max-bandwidth}
+but can be used for setting individual limits
+for selected interfaces. This is an association list of interfaces
+as strings to integer values. For example,
+@lisp
+(max-bw
+ `((\"eth0\" . 15000)
+   (\"ppp0\" . 10000)))
+@end lisp
+@var{bandwidth-detection}
+is disabled on an interface specific level for each
+@var{max-bw}
+configuration. Value range: @samp{0}..@samp{50000}"
+   (serializer
+    (lambda (field-name value)
+      (if (maybe-value-set? value)
+          (vnstat-serialize-alist field-name value) ""))))
+
+  (monthly-months
+   (maybe-integer 25)
+   "\
+Data retention duration for the one month resolution entries. The configuration
+defines for how many past months entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (month-rotate
+   (maybe-integer 1)
+   "\
+Day of month that months are expected to change. Usually set to
+1 but can be set to alternative values for example for tracking
+monthly billed traffic where the billing period doesn't start on
+the first day. For example, if set to 7, days of February up to and
+including the 6th will count for January. Changing this option will
+not cause existing data to be recalculated. Value range: @samp{1}..@samp{28}")
+
+  (month-rotate-affects-years?
+   (maybe-boolean #f)
+   "\
+Enable or disable
+@var{month-rotate}
+also affecting yearly data. Applicable only when
+@var{month-rotate}
+has a value greater than one.")
+
+  (offline-save-interval
+   (maybe-integer 30)
+   "\
+How often in minutes cached interface data is saved to file when all monitored
+interfaces are offline. Value range:
+@var{save-interval}..@samp{60}")
+
+  (pid-file
+   (maybe-string "/var/run/vnstat/vnstatd.pid")
+   "\
+Specify pid file path and name to be used.")
+
+  (poll-interval
+   (maybe-integer 5)
+   "\
+How often in seconds interfaces are checked for status changes.
+Value range: @samp{2}..@samp{60}")
+
+  (rescan-database-on-save?
+   maybe-boolean
+   "\
+Automatically discover added interfaces from the database and start monitoring.
+The rescan is done every
+@var{save-interval}
+or
+@var{offline-save-interval}
+minutes depending on the current activity state.")
+
+  (save-interval
+   (maybe-integer 5)
+   "\
+How often in minutes cached interface data is saved to file.
+Value range: (
+@var{update-interval} / 60 )..@samp{60}")
+
+  (save-on-status-change?
+   (maybe-boolean #t)
+   "\
+Enable or disable the additional saving to file of cached interface data
+when the availability of an interface changes, i.e., when an interface goes
+offline or comes online.")
+
+  (time-sync-wait
+   (maybe-integer 5)
+   "\
+How many minutes to wait during daemon startup for system clock to sync if
+most recent database update appears to be in the future. This may be needed
+in systems without a real-time clock (RTC) which require some time after boot
+to query and set the correct time. @code{0} = wait disabled.
+Value range: @samp{0}..@samp{60}")
+
+  (top-day-entries
+   (maybe-integer 20)
+   "\
+Data retention duration for the top day entries. The configuration
+defines how many of the past top day entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (trafficless-entries?
+   (maybe-boolean #t)
+   "\
+Create database entries even when there is no traffic during the entry's time
+period.")
+
+  (update-file-owner?
+   (maybe-boolean #t)
+   "\
+Enable or disable the update of file ownership during daemon process startup.
+During daemon startup, only database, log and pid files will be modified if the
+user or group change feature (
+@var{daemon-user}
+or
+@var{daemon-group}
+) is enabled and the files don't match the requested user or group. During manual
+database creation, this option will cause file ownership to be inherited from the
+database directory if the directory already exists. This option only has effect
+when the process is started as root or via sudo.")
+
+  (update-interval
+   (maybe-integer 20)
+   "\
+How often in seconds the interface data is updated. Value range:
+@var{poll-interval}..@samp{300}")
+
+  (use-logging
+   (maybe-integer 2)
+   "\
+Enable or disable logging. Accepted values are:
+@code{0} = disabled, @code{1} = logfile and @code{2} = syslog.")
+
+  (use-utc?
+   maybe-boolean
+   "\
+Enable or disable using UTC as timezone in the database for all entries. When
+enabled, all entries added to the database will use UTC regardless of the
+configured system timezone. When disabled, the configured system timezone
+will be used. Changing this setting will not result in already existing
+data to be modified."
+   (serializer
+    (lambda (_ value)
+      (if (maybe-value-set? value)
+          (vnstat-serialize-boolean 'use-UTC value) ""))))
+
+  (yearly-years
+   (maybe-integer -1)
+   "\
+Data retention duration for the one year resolution entries. The configuration
+defines for how many past years entries will be stored. Set to @code{-1} for
+unlimited entries or to @code{0} to disable the data collection of this
+resolution.")
+
+  (prefix vnstat-))
+
+(define (vnstat-serialize-configuration config)
+  (mixed-text-file
+   "vnstat.conf"
+   (serialize-configuration config vnstat-configuration-fields)))
+
+(define (vnstat-shepherd-service config)
+  (let ((config-file (vnstat-serialize-configuration config)))
+    (match-record config <vnstat-configuration> (package pid-file)
+      (shepherd-service
+       (documentation "Run vnstatd.")
+       (requirement `(networking file-systems))
+       (provision '(vnstatd))
+       (start #~(make-forkexec-constructor
+                 (list #$(file-append package "/sbin/vnstatd")
+                       "--daemon"
+                       "--config" #$config-file)
+                 #:pid-file #$pid-file))
+       (stop #~(make-kill-destructor))
+       (actions
+        (list (shepherd-configuration-action config-file)
+              (shepherd-action
+               (name 'reload)
+               (documentation "Reload vnstatd.")
+               (procedure
+                #~(lambda (pid)
+                    (if pid
+                        (begin
+                          (kill pid SIGHUP)
+                          (format #t
+                                  "Issued SIGHUP to vnstatd (PID ~a)."
+                                  pid))
+                        (format #t "vnstatd is not running.")))))))))))
+
+(define (vnstat-account-service config)
+  (match-record config <vnstat-configuration> (daemon-group daemon-user)
+    (filter-map maybe-value (list daemon-group daemon-user))))
+
+(define vnstat-service-type
+  (service-type
+   (name 'vnstat)
+   (description "vnStat network-traffic monitor service.")
+   (extensions
+    (list (service-extension shepherd-root-service-type
+                             (compose list vnstat-shepherd-service))
+          (service-extension account-service-type
+                             vnstat-account-service)))
+   (default-value (vnstat-configuration))))
+
 \f
 ;;;
 ;;; Zabbix server

base-commit: 3c91f4ca490a7ac56dc0aebbca9c4bf4df201877
-- 
2.39.2





^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH v10 2/3] services: inetd: Export accessors.
  2023-01-13 20:07 [bug#60788] [PATCH] services: Add vnstat-service-type Bruno Victal
                   ` (11 preceding siblings ...)
  2023-05-05  0:18 ` [bug#60788] [PATCH v10 1/3] " Bruno Victal
@ 2023-05-05  0:18 ` Bruno Victal
  2023-05-05  0:18 ` [bug#60788] [PATCH v10 3/3] tests: Add vnstat tests Bruno Victal
  13 siblings, 0 replies; 27+ messages in thread
From: Bruno Victal @ 2023-05-05  0:18 UTC (permalink / raw)
  To: 60788; +Cc: ludo, Bruno Victal, maxim.cournoyer

* gnu/services/networking.scm: Export inetd-configuration?,
inetd-configuration-program, inetd-configuration-entries, inetd-entry?,
inetd-entry-node, inetd-entry-name, inetd-entry-socket-type,
inetd-entry-protocol, inetd-entry-wait?, inetd-entry-user, inetd-entry-program
and inetd-entry-arguments.
---
 gnu/services/networking.scm | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/gnu/services/networking.scm b/gnu/services/networking.scm
index 866368aa90..554ab18234 100644
--- a/gnu/services/networking.scm
+++ b/gnu/services/networking.scm
@@ -121,7 +121,19 @@ (define-module (gnu services networking)
             openntpd-service-type
 
             inetd-configuration
+            inetd-configuration?
+            inetd-configuration-program
+            inetd-configuration-entries
             inetd-entry
+            inetd-entry?
+            inetd-entry-node
+            inetd-entry-name
+            inetd-entry-socket-type
+            inetd-entry-protocol
+            inetd-entry-wait?
+            inetd-entry-user
+            inetd-entry-program
+            inetd-entry-arguments
             inetd-service-type
 
             opendht-configuration
-- 
2.39.2





^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [bug#60788] [PATCH v10 3/3] tests: Add vnstat tests.
  2023-01-13 20:07 [bug#60788] [PATCH] services: Add vnstat-service-type Bruno Victal
                   ` (12 preceding siblings ...)
  2023-05-05  0:18 ` [bug#60788] [PATCH v10 2/3] services: inetd: Export accessors Bruno Victal
@ 2023-05-05  0:18 ` Bruno Victal
  13 siblings, 0 replies; 27+ messages in thread
From: Bruno Victal @ 2023-05-05  0:18 UTC (permalink / raw)
  To: 60788; +Cc: ludo, Bruno Victal, maxim.cournoyer

* gnu/tests/vnstat.scm: New file.
* gnu/local.mk: Register it.
---
 gnu/local.mk         |   1 +
 gnu/tests/vnstat.scm | 159 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 160 insertions(+)
 create mode 100644 gnu/tests/vnstat.scm

diff --git a/gnu/local.mk b/gnu/local.mk
index 5f5de953d7..c462c41b9d 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -795,6 +795,7 @@ GNU_SYSTEM_MODULES =				\
   %D%/tests/version-control.scm			\
   %D%/tests/virtualization.scm			\
   %D%/tests/vnc.scm				\
+  %D%/tests/vnstat.scm				\
   %D%/tests/web.scm
 
 INSTALLER_MODULES =                             \
diff --git a/gnu/tests/vnstat.scm b/gnu/tests/vnstat.scm
new file mode 100644
index 0000000000..d1be98e342
--- /dev/null
+++ b/gnu/tests/vnstat.scm
@@ -0,0 +1,159 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2023 Bruno Victal <mirai@makinata.eu>.
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu tests vnstat)
+  #:use-module (gnu tests)
+  #:use-module ((gnu packages networking) #:select (socat vnstat))
+  #:use-module (gnu services)
+  #:use-module (gnu services networking)
+  #:use-module (gnu services monitoring)
+  #:use-module (gnu system)
+  #:use-module (gnu system vm)
+  #:use-module (guix gexp)
+  #:use-module (ice-9 format)
+  #:export (%test-vnstat))
+
+
+(define (run-vnstat-test)
+  "Run tests in a vm which has vnstat running."
+
+  (define vnstat-config
+    (vnstat-configuration
+     (max-bandwidth 0)
+     (time-sync-wait 0)
+     (bandwidth-detection-interval 0)))
+
+  (define inetd-service-entry-config
+    (inetd-entry
+     (name "discard")
+     (socket-type 'stream)
+     (protocol "tcp")
+     (wait? #t)
+     (user "nobody")))
+
+  (define os
+    (marionette-operating-system
+     (simple-operating-system
+      (service dhcp-client-service-type)
+      (service vnstat-service-type
+               vnstat-config)
+      (service inetd-service-type
+               (inetd-configuration
+                (entries
+                 (list inetd-service-entry-config)))))
+     #:imported-modules '((gnu services herd))))
+
+  (define forwarded-port 9999)
+
+  (define vm
+    (let* ((inetd-service-name "discard")
+           (inetd-service-proto
+            (inetd-entry-protocol inetd-service-entry-config))
+           (guest-port
+            (servent:port (getservbyname inetd-service-name
+                                         inetd-service-proto))))
+      (virtual-machine
+       (operating-system os)
+       (port-forwardings `((,forwarded-port . ,guest-port))))))
+
+  ;; The test duration is inconsistent, at times a test may complete under
+  ;; 2 minutes and at times it may take up to 5 minutes.
+  (define test-timeout (* 60 5))
+
+  (define test
+    (with-imported-modules '((gnu build marionette))
+      #~(begin
+          (use-modules (gnu build marionette)
+                       (srfi srfi-64))
+
+          (let ((marionette (make-marionette (list #$vm)))
+                (pid-file #$(vnstat-configuration-pid-file vnstat-config)))
+
+            (test-runner-current (system-test-runner #$output))
+            (test-begin "vnstat")
+
+            (test-assert "service is running"
+              (marionette-eval
+               '(begin
+                  (use-modules (gnu services herd))
+                  (start-service 'vnstatd))
+               marionette))
+
+            (test-assert "vnstatd ready"
+              (wait-for-file pid-file marionette))
+
+            ;; Pump garbage into the 'discard' inetd service within the vm.
+            (let* ((socat #$(file-append socat "/bin/socat"))
+                   (dest-addr #$(format #f "TCP4:localhost:~d"
+                                        forwarded-port))
+                   (args `("socat" "-u" "/dev/zero" ,dest-addr))
+                   ;; XXX: Guile bug (22/03/2023, Guile 3.0.9)
+                   ;; Fixed in main: <https://issues.guix.gnu.org/61073>
+                   ;; FIXME: re-add #:output (%make-void-port "w") below on
+                   ;; next Guile release.
+                   (garbage-pump-pid
+                    (spawn socat args)))
+              (test-group-with-cleanup "Logging"
+                ;; To aid debugging, this test returns #t on success
+                ;; and either #f or 'timed-out otherwise.
+                (test-eq "vnstatd is logging"
+                  #t
+                  (marionette-eval
+                   '(begin
+                      (use-modules (ice-9 popen)
+                                   (ice-9 match)
+                                   (sxml simple)
+                                   (sxml xpath))
+
+                      (define selector
+                        (let ((xpath '(vnstat interface traffic total)))
+                          (compose (node-pos 1) (sxpath xpath))))
+
+                      (let loop ((i 0))
+                        (let* ((vnstat #$(file-append vnstat "/bin/vnstat"))
+                               (query-cmd (format #f "~a --xml" vnstat))
+                               (proc (compose selector xml->sxml))
+                               (result
+                                (call-with-port
+                                    (open-input-pipe query-cmd) proc)))
+                          (match result
+                            ;; Counter still warming up.
+                            ((('total ('rx "0") ('tx "0")))
+                             (sleep 1)
+                             (if (< i #$test-timeout)
+                                 (loop (+ i 1))
+                                 'timed-out))
+                            ;; Count of bytes on iface was non-zero.
+                            ((('total ('rx rx) ('tx tx)))
+                             #t)
+                            ;; Unknown data encountered, perhaps the
+                            ;; data format changed?
+                            (_ #f)))))
+                   marionette))
+                ;; Cleanup: shutdown garbage pump.
+                (kill garbage-pump-pid SIGTERM)))
+
+            (test-end)))))
+
+  (gexp->derivation "vnstat-test" test))
+
+(define %test-vnstat
+  (system-test
+   (name "vnstat")
+   (description "Basic tests for vnstat service.")
+   (value (run-vnstat-test))))
-- 
2.39.2





^ permalink raw reply related	[flat|nested] 27+ messages in thread

* bug#60788: [PATCH v10 1/3] services: Add vnstat-service-type.
  2023-05-05  0:18 ` [bug#60788] [PATCH v10 1/3] " Bruno Victal
@ 2023-05-11 14:33   ` Ludovic Courtès
  0 siblings, 0 replies; 27+ messages in thread
From: Ludovic Courtès @ 2023-05-11 14:33 UTC (permalink / raw)
  To: Bruno Victal; +Cc: maxim.cournoyer, 60788-done

Hi,

Bruno Victal <mirai@makinata.eu> skribis:

> * gnu/services/monitoring.scm (vnstat-service-type): New variable.
> * doc/guix.texi (Monitoring Services): Document it.

[...]

> * gnu/tests/vnstat.scm: New file.
> * gnu/local.mk: Register it.

[...]

> * gnu/services/networking.scm: Export inetd-configuration?,
> inetd-configuration-program, inetd-configuration-entries, inetd-entry?,
> inetd-entry-node, inetd-entry-name, inetd-entry-socket-type,
> inetd-entry-protocol, inetd-entry-wait?, inetd-entry-user, inetd-entry-program
> and inetd-entry-arguments.

Applied all three patches.  Pheww, that was a long ride!  Thanks for
your patience and for your quality work!

Ludo’.




^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2023-05-11 14:34 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-01-13 20:07 [bug#60788] [PATCH] services: Add vnstat-service-type Bruno Victal
2023-01-14 21:06 ` [bug#60788] [PATCH v2] " Bruno Victal
2023-01-16 18:42 ` [bug#60788] [PATCH] " Maxim Cournoyer
2023-01-16 19:31   ` Bruno Victal
2023-01-16 19:56     ` Maxim Cournoyer
2023-01-18  0:34 ` [bug#60788] [PATCH v2] " Bruno Victal
2023-01-18  0:37 ` [bug#60788] [PATCH v2] services: vnstat: Use least-authority-wrapper Bruno Victal
2023-02-02 14:21 ` [bug#60788] [PATCH v3] services: Add vnstat-service-type Bruno Victal
2023-02-07 14:25 ` [bug#60788] [PATCH v4] " Bruno Victal
2023-02-09  3:34   ` [bug#60788] [PATCH] " Maxim Cournoyer
2023-02-09  4:19     ` Bruno Victal
2023-02-09 13:31       ` Maxim Cournoyer
2023-02-10 13:15 ` [bug#60788] [PATCH v5] " Bruno Victal
2023-02-10 14:07   ` Maxim Cournoyer
2023-02-10 14:14 ` [bug#60788] [PATCH v6] " Bruno Victal
2023-03-22 16:15 ` [bug#60788] [PATCH v7] " Bruno Victal
2023-04-03 14:14 ` [bug#60788] [PATCH v8] " Bruno Victal
2023-04-04 13:08 ` [bug#60788] [PATCH v9] " Bruno Victal
2023-04-07 15:22   ` [bug#60788] [PATCH] " Ludovic Courtès
2023-04-07 20:04     ` Maxim Cournoyer
2023-04-20 10:03       ` [bug#60788] Policy for system tests? Ludovic Courtès
2023-04-08 12:40     ` [bug#60788] [PATCH] services: Add vnstat-service-type Bruno Victal
2023-04-20 10:09       ` Ludovic Courtès
2023-05-05  0:18 ` [bug#60788] [PATCH v10 1/3] " Bruno Victal
2023-05-11 14:33   ` bug#60788: " Ludovic Courtès
2023-05-05  0:18 ` [bug#60788] [PATCH v10 2/3] services: inetd: Export accessors Bruno Victal
2023-05-05  0:18 ` [bug#60788] [PATCH v10 3/3] tests: Add vnstat tests Bruno Victal

Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/guix.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.