mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-02-12 08:01:50 +00:00
nixos/atop: Add configuration for atop services, allow to enable netatop, gpuatop, allow setuid wrapper
This commit is contained in:
parent
327dcea4cc
commit
8f3d2e5c3b
nixos
|
@ -1,6 +1,6 @@
|
||||||
# Global configuration for atop.
|
# Global configuration for atop.
|
||||||
|
|
||||||
{ config, lib, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
with lib;
|
with lib;
|
||||||
|
|
||||||
|
@ -12,11 +12,82 @@ in
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
|
|
||||||
programs.atop = {
|
programs.atop = rec {
|
||||||
|
|
||||||
|
enable = mkEnableOption "Atop";
|
||||||
|
|
||||||
|
package = mkOption {
|
||||||
|
type = types.package;
|
||||||
|
default = pkgs.atop;
|
||||||
|
description = ''
|
||||||
|
Which package to use for Atop.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
netatop = {
|
||||||
|
enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Whether to install and enable the netatop kernel module.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
package = mkOption {
|
||||||
|
type = types.package;
|
||||||
|
default = config.boot.kernelPackages.netatop;
|
||||||
|
description = ''
|
||||||
|
Which package to use for netatop.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
atopgpu.enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Whether to install and enable the atopgpud daemon to get information about
|
||||||
|
NVIDIA gpus.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
setuidWrapper.enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = cfg.netatop.enable || cfg.atopgpu.enable;
|
||||||
|
description = ''
|
||||||
|
Whether to install a setuid wrapper for Atop. This is required to use some of
|
||||||
|
the features as non-root user (e.g.: ipc information, netatop, atopgpu).
|
||||||
|
Atop tries to drop the root privileges shortly after starting.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
atopsvc.enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
Whether to enable the atop service responsible for storing statistics for
|
||||||
|
long-term analysis.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
atopRotate.enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
Whether to enable the atop-rotate timer, which restarts the atop service
|
||||||
|
daily to make sure the data files are rotate.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
atopacct.enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
Whether to enable the atopacct service which manages process accounting.
|
||||||
|
This allows Atop to gather data about processes that disappeared in between
|
||||||
|
two refresh intervals.
|
||||||
|
'';
|
||||||
|
};
|
||||||
settings = mkOption {
|
settings = mkOption {
|
||||||
type = types.attrs;
|
type = types.attrs;
|
||||||
default = {};
|
default = { };
|
||||||
example = {
|
example = {
|
||||||
flags = "a1f";
|
flags = "a1f";
|
||||||
interval = 5;
|
interval = 5;
|
||||||
|
@ -25,12 +96,51 @@ in
|
||||||
Parameters to be written to <filename>/etc/atoprc</filename>.
|
Parameters to be written to <filename>/etc/atoprc</filename>.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf (cfg.settings != {}) {
|
config = mkIf cfg.enable (
|
||||||
environment.etc.atoprc.text =
|
let
|
||||||
concatStrings (mapAttrsToList (n: v: "${n} ${toString v}\n") cfg.settings);
|
atop =
|
||||||
};
|
if cfg.atopgpu.enable then
|
||||||
|
(cfg.package.override { withAtopgpu = true; })
|
||||||
|
else
|
||||||
|
cfg.package;
|
||||||
|
packages = [ atop (lib.mkIf cfg.netatop.enable cfg.netatop.package) ];
|
||||||
|
in
|
||||||
|
{
|
||||||
|
environment.etc = mkIf (cfg.settings != { }) {
|
||||||
|
atoprc.text = concatStrings
|
||||||
|
(mapAttrsToList
|
||||||
|
(n: v: ''
|
||||||
|
${n} ${toString v}
|
||||||
|
'')
|
||||||
|
cfg.settings);
|
||||||
|
};
|
||||||
|
environment.systemPackages = packages;
|
||||||
|
boot.extraModulePackages = [ (lib.mkIf cfg.netatop.enable cfg.netatop.package) ];
|
||||||
|
systemd =
|
||||||
|
let
|
||||||
|
mkSystemd = type: cond: name: {
|
||||||
|
${name} = lib.mkIf cond {
|
||||||
|
restartTriggers = packages;
|
||||||
|
wantedBy = [ (if type == "services" then "multi-user.target" else if type == "timers" then "timers.target" else null) ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
mkService = mkSystemd "services";
|
||||||
|
mkTimer = mkSystemd "timers";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
inherit packages;
|
||||||
|
services =
|
||||||
|
mkService cfg.atopsvc.enable "atop"
|
||||||
|
// mkService cfg.atopacct.enable "atopacct"
|
||||||
|
// mkService cfg.netatop.enable "netatop"
|
||||||
|
// mkService cfg.atopgpu.enable "atopgpu";
|
||||||
|
timers = mkTimer cfg.atopRotate.enable "atop-rotate";
|
||||||
|
};
|
||||||
|
security.wrappers =
|
||||||
|
lib.mkIf cfg.setuidWrapper.enable { atop = { source = "${atop}/bin/atop"; }; };
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ in
|
||||||
amazon-init-shell = handleTest ./amazon-init-shell.nix {};
|
amazon-init-shell = handleTest ./amazon-init-shell.nix {};
|
||||||
ammonite = handleTest ./ammonite.nix {};
|
ammonite = handleTest ./ammonite.nix {};
|
||||||
atd = handleTest ./atd.nix {};
|
atd = handleTest ./atd.nix {};
|
||||||
|
atop = handleTest ./atop.nix {};
|
||||||
avahi = handleTest ./avahi.nix {};
|
avahi = handleTest ./avahi.nix {};
|
||||||
avahi-with-resolved = handleTest ./avahi.nix { networkd = true; };
|
avahi-with-resolved = handleTest ./avahi.nix { networkd = true; };
|
||||||
awscli = handleTest ./awscli.nix { };
|
awscli = handleTest ./awscli.nix { };
|
||||||
|
|
132
nixos/tests/atop.nix
Normal file
132
nixos/tests/atop.nix
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
import ./make-test-python.nix ({ pkgs, ... }: {
|
||||||
|
name = "atop";
|
||||||
|
|
||||||
|
nodes = {
|
||||||
|
defaults = { ... }: {
|
||||||
|
programs.atop = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
minimal = { ... }: {
|
||||||
|
programs.atop = {
|
||||||
|
enable = true;
|
||||||
|
atopsvc.enable = false;
|
||||||
|
atopRotate.enable = false;
|
||||||
|
atopacct.enable = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
minimal_with_setuid = { ... }: {
|
||||||
|
programs.atop = {
|
||||||
|
enable = true;
|
||||||
|
atopsvc.enable = false;
|
||||||
|
atopRotate.enable = false;
|
||||||
|
atopacct.enable = false;
|
||||||
|
setuidWrapper.enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
atoprc_and_netatop = { ... }: {
|
||||||
|
programs.atop = {
|
||||||
|
enable = true;
|
||||||
|
netatop.enable = true;
|
||||||
|
settings = {
|
||||||
|
flags = "faf1";
|
||||||
|
interval = 2;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
atopgpu = { lib, ... }: {
|
||||||
|
nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
|
||||||
|
"cudatoolkit"
|
||||||
|
];
|
||||||
|
programs.atop = {
|
||||||
|
enable = true;
|
||||||
|
atopgpu.enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript = ''
|
||||||
|
def a_version(m):
|
||||||
|
v = m.succeed("atop -V")
|
||||||
|
pkgver = "${pkgs.atop.version}"
|
||||||
|
assert v.startswith("Version: {}".format(pkgver)), "Version is {}, expected `{}`".format(v, pkgver)
|
||||||
|
|
||||||
|
def __exp_path(m, prg, expected):
|
||||||
|
p = m.succeed("type -p \"{}\" | head -c -1".format(prg))
|
||||||
|
assert p == expected, "{} is `{}`, expected `{}`".format(prg, p, expected)
|
||||||
|
|
||||||
|
def a_setuid(m, present=True):
|
||||||
|
if present:
|
||||||
|
__exp_path(m, "atop", "/run/wrappers/bin/atop")
|
||||||
|
stat = m.succeed("stat --printf '%a %u' /run/wrappers/bin/atop")
|
||||||
|
assert stat == "4511 0", "Wrapper stat is {}, expected `4511 0`".format(stat)
|
||||||
|
else:
|
||||||
|
__exp_path(m, "atop", "/run/current-system/sw/bin/atop")
|
||||||
|
|
||||||
|
def assert_no_netatop(m):
|
||||||
|
m.require_unit_state("netatop.service", "inactive")
|
||||||
|
m.fail("modprobe -n -v netatop")
|
||||||
|
|
||||||
|
def a_netatop(m, present=True):
|
||||||
|
m.require_unit_state("netatop.service", "active" if present else "inactive")
|
||||||
|
if present:
|
||||||
|
out = m.succeed("modprobe -n -v netatop")
|
||||||
|
assert out == "", "Module should be loaded, but modprobe would have done `{}`.".format(out)
|
||||||
|
else:
|
||||||
|
m.fail("modprobe -n -v netatop")
|
||||||
|
|
||||||
|
def a_atopgpu(m, present=True):
|
||||||
|
m.require_unit_state("atopgpu.service", "active" if present else "inactive")
|
||||||
|
if present:
|
||||||
|
__exp_path(m, "atopgpud", "/run/current-system/sw/bin/atopgpud")
|
||||||
|
|
||||||
|
# atop.service should log some data to /var/log/atop
|
||||||
|
def a_atopsvc(m, present=True):
|
||||||
|
m.require_unit_state("atop.service", "active" if present else "inactive")
|
||||||
|
if present:
|
||||||
|
files = int(m.succeed("ls -1 /var/log/atop | wc -l"))
|
||||||
|
assert files >= 1, "Expected at least 1 data file"
|
||||||
|
# def check_files(_):
|
||||||
|
# files = int(m.succeed("ls -1 /var/log/atop | wc -l"))
|
||||||
|
# return files >= 1
|
||||||
|
# retry(check_files)
|
||||||
|
|
||||||
|
def a_atoprotate(m, present=True):
|
||||||
|
m.require_unit_state("atop-rotate.timer", "active" if present else "inactive")
|
||||||
|
|
||||||
|
# atopacct.service should make kernel write to /run/pacct_source and make dir
|
||||||
|
# /run/pacct_shadow.d
|
||||||
|
def a_atopacct(m, present=True):
|
||||||
|
m.require_unit_state("atopacct.service", "active" if present else "inactive")
|
||||||
|
if present:
|
||||||
|
m.succeed("test -f /run/pacct_source")
|
||||||
|
files = int(m.succeed("ls -1 /run/pacct_shadow.d | wc -l"))
|
||||||
|
assert files >= 1, "Expected at least 1 pacct_shadow.d file"
|
||||||
|
|
||||||
|
def a_atoprc(m, contents):
|
||||||
|
if contents:
|
||||||
|
f = m.succeed("cat /etc/atoprc")
|
||||||
|
assert f == contents, "/etc/atoprc contents: `{}`, expected `{}`".format(f, contents)
|
||||||
|
else:
|
||||||
|
m.succeed("test ! -e /etc/atoprc")
|
||||||
|
|
||||||
|
def assert_all(m, setuid, atopsvc, atoprotate, atopacct, netatop, atopgpu, atoprc):
|
||||||
|
a_version(m)
|
||||||
|
a_setuid(m, setuid)
|
||||||
|
a_atopsvc(m, atopsvc)
|
||||||
|
a_atoprotate(m, atoprotate)
|
||||||
|
a_atopacct(m, atopacct)
|
||||||
|
a_netatop(m, netatop)
|
||||||
|
a_atopgpu(m, atopgpu)
|
||||||
|
a_atoprc(m, atoprc)
|
||||||
|
|
||||||
|
assert_all(defaults, False, True, True, True, False, False, False)
|
||||||
|
assert_all(minimal, False, False, False, False, False, False, False)
|
||||||
|
assert_all(minimal_with_setuid, True, False, False, False, False, False, False)
|
||||||
|
assert_all(atoprc_and_netatop, False, True, True, True, True, False,
|
||||||
|
"flags faf1\ninterval 2\n")
|
||||||
|
assert_all(atopgpu, False, True, True, True, False, True, False)
|
||||||
|
'';
|
||||||
|
})
|
Loading…
Reference in a new issue