Merge pull request #76551 from symphorien/nagios2

nagios: various improvements

Changed files
+192 -50
nixos
modules
services
monitoring
tests
pkgs
servers
monitoring
nagios
+64 -49
nixos/modules/services/monitoring/nagios.nix
···
preferLocalBuild = true;
} "mkdir -p $out; ln -s $nagiosObjectDefs $out/";
-
nagiosCfgFile = pkgs.writeText "nagios.cfg"
-
''
-
# Paths for state and logs.
-
log_file=${nagiosLogDir}/current
-
log_archive_path=${nagiosLogDir}/archive
-
status_file=${nagiosState}/status.dat
-
object_cache_file=${nagiosState}/objects.cache
-
temp_file=${nagiosState}/nagios.tmp
-
lock_file=/run/nagios.lock # Not used I think.
-
state_retention_file=${nagiosState}/retention.dat
-
query_socket=${nagiosState}/nagios.qh
-
check_result_path=${nagiosState}
-
command_file=${nagiosState}/nagios.cmd
-
-
# Configuration files.
-
#resource_file=resource.cfg
-
cfg_dir=${nagiosObjectDefsDir}
-
-
# Uid/gid that the daemon runs under.
-
nagios_user=nagios
-
nagios_group=nagios
-
-
# Misc. options.
-
illegal_macro_output_chars=`~$&|'"<>
-
retain_state_information=1
-
''; # "
+
nagiosCfgFile = let
+
default = {
+
log_file="${nagiosLogDir}/current";
+
log_archive_path="${nagiosLogDir}/archive";
+
status_file="${nagiosState}/status.dat";
+
object_cache_file="${nagiosState}/objects.cache";
+
temp_file="${nagiosState}/nagios.tmp";
+
lock_file="/run/nagios.lock";
+
state_retention_file="${nagiosState}/retention.dat";
+
query_socket="${nagiosState}/nagios.qh";
+
check_result_path="${nagiosState}";
+
command_file="${nagiosState}/nagios.cmd";
+
cfg_dir="${nagiosObjectDefsDir}";
+
nagios_user="nagios";
+
nagios_group="nagios";
+
illegal_macro_output_chars="`~$&|'\"<>";
+
retain_state_information="1";
+
};
+
lines = mapAttrsToList (key: value: "${key}=${value}") (default // cfg.extraConfig);
+
content = concatStringsSep "\n" lines;
+
file = pkgs.writeText "nagios.cfg" content;
+
validated = pkgs.runCommand "nagios-checked.cfg" {preferLocalBuild=true;} ''
+
cp ${file} nagios.cfg
+
# nagios checks the existence of /var/lib/nagios, but
+
# it does not exists in the build sandbox, so we fake it
+
mkdir lib
+
lib=$(readlink -f lib)
+
sed -i s@=${nagiosState}@=$lib@ nagios.cfg
+
${pkgs.nagios}/bin/nagios -v nagios.cfg && cp ${file} $out
+
'';
+
defaultCfgFile = if cfg.validateConfig then validated else file;
+
in
+
if cfg.mainConfigFile == null then defaultCfgFile else cfg.mainConfigFile;
# Plain configuration for the Nagios web-interface with no
# authentication.
···
(mkRemovedOptionModule [ "services" "nagios" "urlPath" ] "The urlPath option has been removed as it is hard coded to /nagios in the nagios package.")
];
+
meta.maintainers = with lib.maintainers; [ symphorien ];
+
options = {
services.nagios = {
-
enable = mkOption {
-
default = false;
-
description = "
-
Whether to use <link
-
xlink:href='http://www.nagios.org/'>Nagios</link> to monitor
-
your system or network.
-
";
-
};
+
enable = mkEnableOption "<link xlink:href='http://www.nagios.org/'>Nagios</link> to monitor your system or network.";
objectDefs = mkOption {
description = "
···
the hosts, host groups, services and contacts for the
network that you want Nagios to monitor.
";
+
type = types.listOf types.path;
+
example = literalExample "[ ./objects.cfg ]";
};
plugins = mkOption {
type = types.listOf types.package;
-
default = [pkgs.nagiosPluginsOfficial pkgs.ssmtp];
-
defaultText = "[pkgs.nagiosPluginsOfficial pkgs.ssmtp]";
+
default = with pkgs; [ nagiosPluginsOfficial ssmtp mailutils ];
+
defaultText = "[pkgs.nagiosPluginsOfficial pkgs.ssmtp pkgs.mailutils]";
description = "
Packages to be added to the Nagios <envar>PATH</envar>.
Typically used to add plugins, but can be anything.
···
};
mainConfigFile = mkOption {
-
type = types.package;
-
default = nagiosCfgFile;
-
defaultText = "nagiosCfgFile";
+
type = types.nullOr types.package;
+
default = null;
description = "
-
Derivation for the main configuration file of Nagios.
+
If non-null, overrides the main configuration file of Nagios.
";
};
+
extraConfig = mkOption {
+
type = types.attrsOf types.str;
+
example = {
+
debug_level = "-1";
+
debug_file = "/var/log/nagios/debug.log";
+
};
+
default = {};
+
description = "Configuration to add to /etc/nagios.cfg";
+
};
+
+
validateConfig = mkOption {
+
type = types.bool;
+
default = pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform;
+
description = "if true, the syntax of the nagios configuration file is checked at build time";
+
};
+
cgiConfigFile = mkOption {
type = types.package;
default = nagiosCGICfgFile;
···
};
enableWebInterface = mkOption {
+
type = types.bool;
default = false;
description = "
Whether to enable the Nagios web interface. You should also
···
# This isn't needed, it's just so that the user can type "nagiostats
# -c /etc/nagios.cfg".
environment.etc = [
-
{ source = cfg.mainConfigFile;
+
{ source = nagiosCfgFile;
target = "nagios.cfg";
}
];
···
environment.systemPackages = [ pkgs.nagios ];
systemd.services.nagios = {
description = "Nagios monitoring daemon";
-
path = [ pkgs.nagios ];
+
path = [ pkgs.nagios ] ++ cfg.plugins;
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
···
RestartSec = 2;
LogsDirectory = "nagios";
StateDirectory = "nagios";
+
ExecStart = "${pkgs.nagios}/bin/nagios /etc/nagios.cfg";
+
X-ReloadIfChanged = nagiosCfgFile;
};
-
-
script = ''
-
for i in ${toString cfg.plugins}; do
-
export PATH=$i/bin:$i/sbin:$i/libexec:$PATH
-
done
-
exec ${pkgs.nagios}/bin/nagios ${cfg.mainConfigFile}
-
'';
};
services.httpd.virtualHosts = optionalAttrs cfg.enableWebInterface {
+1
nixos/tests/all-tests.nix
···
mysql = handleTest ./mysql.nix {};
mysqlBackup = handleTest ./mysql-backup.nix {};
mysqlReplication = handleTest ./mysql-replication.nix {};
+
nagios = handleTest ./nagios.nix {};
nat.firewall = handleTest ./nat.nix { withFirewall = true; };
nat.firewall-conntrack = handleTest ./nat.nix { withFirewall = true; withConntrackHelpers = true; };
nat.standalone = handleTest ./nat.nix { withFirewall = false; };
+116
nixos/tests/nagios.nix
···
+
import ./make-test-python.nix (
+
{ pkgs, ... }: {
+
name = "nagios";
+
meta = with pkgs.stdenv.lib.maintainers; {
+
maintainers = [ symphorien ];
+
};
+
+
machine = { lib, ... }: let
+
writer = pkgs.writeShellScript "write" ''
+
set -x
+
echo "$@" >> /tmp/notifications
+
'';
+
in
+
{
+
# tested service
+
services.sshd.enable = true;
+
# nagios
+
services.nagios = {
+
enable = true;
+
# make state transitions faster
+
extraConfig.interval_length = "5";
+
objectDefs =
+
(map (x: "${pkgs.nagios}/etc/objects/${x}.cfg") [ "templates" "timeperiods" "commands" ]) ++ [
+
(
+
pkgs.writeText "objects.cfg" ''
+
# notifications are written to /tmp/notifications
+
define command {
+
command_name notify-host-by-file
+
command_line ${writer} "$HOSTNAME is $HOSTSTATE$"
+
}
+
define command {
+
command_name notify-service-by-file
+
command_line ${writer} "$SERVICEDESC$ is $SERVICESTATE$"
+
}
+
+
# nagios boilerplate
+
define contact {
+
contact_name alice
+
alias alice
+
host_notifications_enabled 1
+
service_notifications_enabled 1
+
service_notification_period 24x7
+
host_notification_period 24x7
+
service_notification_options w,u,c,r,f,s
+
host_notification_options d,u,r,f,s
+
service_notification_commands notify-service-by-file
+
host_notification_commands notify-host-by-file
+
email foo@example.com
+
}
+
define contactgroup {
+
contactgroup_name admins
+
alias Admins
+
members alice
+
}
+
define hostgroup{
+
hostgroup_name allhosts
+
alias All hosts
+
}
+
+
# monitored objects
+
define host {
+
use generic-host
+
host_name localhost
+
alias localhost
+
address localhost
+
hostgroups allhosts
+
contact_groups admins
+
# make state transitions faster.
+
max_check_attempts 2
+
check_interval 1
+
retry_interval 1
+
}
+
define service {
+
use generic-service
+
host_name localhost
+
service_description ssh
+
check_command check_ssh
+
# make state transitions faster.
+
max_check_attempts 2
+
check_interval 1
+
retry_interval 1
+
}
+
''
+
)
+
];
+
};
+
};
+
+
testScript = { ... }: ''
+
with subtest("ensure sshd starts"):
+
machine.wait_for_unit("sshd.service")
+
+
+
with subtest("ensure nagios starts"):
+
machine.wait_for_file("/var/log/nagios/current")
+
+
+
def assert_notify(text):
+
machine.wait_for_file("/tmp/notifications")
+
real = machine.succeed("cat /tmp/notifications").strip()
+
print(f"got {real!r}, expected {text!r}")
+
assert text == real
+
+
+
with subtest("ensure we get a notification when sshd is down"):
+
machine.succeed("systemctl stop sshd")
+
assert_notify("ssh is CRITICAL")
+
+
+
with subtest("ensure tests can succeed"):
+
machine.succeed("systemctl start sshd")
+
machine.succeed("rm /tmp/notifications")
+
assert_notify("ssh is OK")
+
'';
+
}
+
)
+11 -1
pkgs/servers/monitoring/nagios/default.nix
···
-
{ stdenv, fetchurl, perl, php, gd, libpng, zlib, unzip }:
+
{ stdenv, fetchurl, perl, php, gd, libpng, zlib, unzip, nixosTests }:
stdenv.mkDerivation rec {
pname = "nagios";
···
substituteInPlace Makefile --replace '$(MAKE) install-basic' ""
'';
installTargets = "install install-config";
+
postInstall = ''
+
# don't make default files use hardcoded paths to commands
+
sed -i 's@command_line *[^ ]*/\([^/]*\) @command_line \1 @' $out/etc/objects/commands.cfg
+
sed -i 's@/usr/bin/@@g' $out/etc/objects/commands.cfg
+
sed -i 's@/bin/@@g' $out/etc/objects/commands.cfg
+
'';
+
+
passthru.tests = {
+
inherit (nixosTests) nagios;
+
};
meta = {
description = "A host, service and network monitoring program";