Merge pull request #123053 from pschyska/master
atop, netatop, nixos/atop: improve packaging and options
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Global configuration for atop.
|
||||
|
||||
{ config, lib, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
@@ -12,11 +12,83 @@ in
|
||||
|
||||
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.
|
||||
Note: this sets the kernel taint flag "O" for loading out-of-tree modules.
|
||||
'';
|
||||
};
|
||||
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 = false;
|
||||
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.
|
||||
'';
|
||||
};
|
||||
|
||||
atopService.enable = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to enable the atop service responsible for storing statistics for
|
||||
long-term analysis.
|
||||
'';
|
||||
};
|
||||
atopRotateTimer.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.
|
||||
'';
|
||||
};
|
||||
atopacctService.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 {
|
||||
type = types.attrs;
|
||||
default = {};
|
||||
default = { };
|
||||
example = {
|
||||
flags = "a1f";
|
||||
interval = 5;
|
||||
@@ -25,12 +97,50 @@ in
|
||||
Parameters to be written to <filename>/etc/atoprc</filename>.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf (cfg.settings != {}) {
|
||||
environment.etc.atoprc.text =
|
||||
concatStrings (mapAttrsToList (n: v: "${n} ${toString v}\n") cfg.settings);
|
||||
};
|
||||
config = mkIf cfg.enable (
|
||||
let
|
||||
atop =
|
||||
if cfg.atopgpu.enable then
|
||||
(cfg.package.override { withAtopgpu = true; })
|
||||
else
|
||||
cfg.package;
|
||||
in
|
||||
{
|
||||
environment.etc = mkIf (cfg.settings != { }) {
|
||||
atoprc.text = concatStrings
|
||||
(mapAttrsToList
|
||||
(n: v: ''
|
||||
${n} ${toString v}
|
||||
'')
|
||||
cfg.settings);
|
||||
};
|
||||
environment.systemPackages = [ atop (lib.mkIf cfg.netatop.enable cfg.netatop.package) ];
|
||||
boot.extraModulePackages = [ (lib.mkIf cfg.netatop.enable cfg.netatop.package) ];
|
||||
systemd =
|
||||
let
|
||||
mkSystemd = type: cond: name: restartTriggers: {
|
||||
${name} = lib.mkIf cond {
|
||||
inherit restartTriggers;
|
||||
wantedBy = [ (if type == "services" then "multi-user.target" else if type == "timers" then "timers.target" else null) ];
|
||||
};
|
||||
};
|
||||
mkService = mkSystemd "services";
|
||||
mkTimer = mkSystemd "timers";
|
||||
in
|
||||
{
|
||||
packages = [ atop (lib.mkIf cfg.netatop.enable cfg.netatop.package) ];
|
||||
services =
|
||||
mkService cfg.atopService.enable "atop" [ atop ]
|
||||
// mkService cfg.atopacctService.enable "atopacct" [ atop ]
|
||||
// mkService cfg.netatop.enable "netatop" [ cfg.netatop.package ]
|
||||
// mkService cfg.atopgpu.enable "atopgpu" [ atop ];
|
||||
timers = mkTimer cfg.atopRotateTimer.enable "atop-rotate" [ atop ];
|
||||
};
|
||||
security.wrappers =
|
||||
lib.mkIf cfg.setuidWrapper.enable { atop = { source = "${atop}/bin/atop"; }; };
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ in
|
||||
ammonite = handleTest ./ammonite.nix {};
|
||||
apparmor = handleTest ./apparmor.nix {};
|
||||
atd = handleTest ./atd.nix {};
|
||||
atop = handleTest ./atop.nix {};
|
||||
avahi = handleTest ./avahi.nix {};
|
||||
avahi-with-resolved = handleTest ./avahi.nix { networkd = true; };
|
||||
awscli = handleTest ./awscli.nix { };
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
{ system ? builtins.currentSystem
|
||||
, config ? { }
|
||||
, pkgs ? import ../.. { inherit system config; }
|
||||
}:
|
||||
|
||||
with import ../lib/testing-python.nix { inherit system pkgs; };
|
||||
with pkgs.lib;
|
||||
|
||||
let assertions = rec {
|
||||
path = program: path: ''
|
||||
with subtest("The path of ${program} should be ${path}"):
|
||||
p = machine.succeed("type -p \"${program}\" | head -c -1")
|
||||
assert p == "${path}", f"${program} is {p}, expected ${path}"
|
||||
'';
|
||||
unit = name: state: ''
|
||||
with subtest("Unit ${name} should be ${state}"):
|
||||
machine.require_unit_state("${name}", "${state}")
|
||||
'';
|
||||
version = ''
|
||||
import re
|
||||
|
||||
with subtest("binary should report the correct version"):
|
||||
pkgver = "${pkgs.atop.version}"
|
||||
ver = re.sub(r'(?s)^Version: (\d\.\d\.\d).*', r'\1', machine.succeed("atop -V"))
|
||||
assert ver == pkgver, f"Version is `{ver}`, expected `{pkgver}`"
|
||||
'';
|
||||
atoprc = contents:
|
||||
if builtins.stringLength contents > 0 then ''
|
||||
with subtest("/etc/atoprc should have the correct contents"):
|
||||
f = machine.succeed("cat /etc/atoprc")
|
||||
assert f == "${contents}", f"/etc/atoprc contents: '{f}', expected '${contents}'"
|
||||
'' else ''
|
||||
with subtest("/etc/atoprc should not be present"):
|
||||
machine.succeed("test ! -e /etc/atoprc")
|
||||
'';
|
||||
wrapper = present:
|
||||
if present then path "atop" "/run/wrappers/bin/atop" + ''
|
||||
with subtest("Wrapper should be setuid root"):
|
||||
stat = machine.succeed("stat --printf '%a %u' /run/wrappers/bin/atop")
|
||||
assert stat == "4511 0", f"Wrapper stat is {stat}, expected '4511 0'"
|
||||
''
|
||||
else path "atop" "/run/current-system/sw/bin/atop";
|
||||
atopService = present:
|
||||
if present then
|
||||
unit "atop.service" "active"
|
||||
+ ''
|
||||
with subtest("atop.service should have written some data to /var/log/atop"):
|
||||
files = int(machine.succeed("ls -1 /var/log/atop | wc -l"))
|
||||
assert files > 0, "Expected at least 1 data file"
|
||||
'' else unit "atop.service" "inactive";
|
||||
atopRotateTimer = present:
|
||||
unit "atop-rotate.timer" (if present then "active" else "inactive");
|
||||
atopacctService = present:
|
||||
if present then
|
||||
unit "atopacct.service" "active"
|
||||
+ ''
|
||||
with subtest("atopacct.service should enable process accounting"):
|
||||
machine.succeed("test -f /run/pacct_source")
|
||||
|
||||
with subtest("atopacct.service should write data to /run/pacct_shadow.d"):
|
||||
files = int(machine.succeed("ls -1 /run/pacct_shadow.d | wc -l"))
|
||||
assert files >= 1, "Expected at least 1 pacct_shadow.d file"
|
||||
'' else unit "atopacct.service" "inactive";
|
||||
netatop = present:
|
||||
if present then
|
||||
unit "netatop.service" "active"
|
||||
+ ''
|
||||
with subtest("The netatop kernel module should be loaded"):
|
||||
out = machine.succeed("modprobe -n -v netatop")
|
||||
assert out == "", f"Module should be loaded already, but modprobe would have done {out}."
|
||||
'' else ''
|
||||
with subtest("The netatop kernel module should be absent"):
|
||||
machine.fail("modprobe -n -v netatop")
|
||||
'';
|
||||
atopgpu = present:
|
||||
if present then
|
||||
(unit "atopgpu.service" "active") + (path "atopgpud" "/run/current-system/sw/bin/atopgpud")
|
||||
else (unit "atopgpu.service" "inactive") + ''
|
||||
with subtest("atopgpud should not be present"):
|
||||
machine.fail("type -p atopgpud")
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
name = "atop";
|
||||
|
||||
justThePackage = makeTest {
|
||||
name = "atop-justThePackage";
|
||||
machine = {
|
||||
environment.systemPackages = [ pkgs.atop ];
|
||||
};
|
||||
testScript = with assertions; builtins.concatStringsSep "\n" [
|
||||
version
|
||||
(atoprc "")
|
||||
(wrapper false)
|
||||
(atopService false)
|
||||
(atopRotateTimer false)
|
||||
(atopacctService false)
|
||||
(netatop false)
|
||||
(atopgpu false)
|
||||
];
|
||||
};
|
||||
defaults = makeTest {
|
||||
name = "atop-defaults";
|
||||
machine = {
|
||||
programs.atop = {
|
||||
enable = true;
|
||||
};
|
||||
};
|
||||
testScript = with assertions; builtins.concatStringsSep "\n" [
|
||||
version
|
||||
(atoprc "")
|
||||
(wrapper false)
|
||||
(atopService true)
|
||||
(atopRotateTimer true)
|
||||
(atopacctService true)
|
||||
(netatop false)
|
||||
(atopgpu false)
|
||||
];
|
||||
};
|
||||
minimal = makeTest {
|
||||
name = "atop-minimal";
|
||||
machine = {
|
||||
programs.atop = {
|
||||
enable = true;
|
||||
atopService.enable = false;
|
||||
atopRotateTimer.enable = false;
|
||||
atopacctService.enable = false;
|
||||
};
|
||||
};
|
||||
testScript = with assertions; builtins.concatStringsSep "\n" [
|
||||
version
|
||||
(atoprc "")
|
||||
(wrapper false)
|
||||
(atopService false)
|
||||
(atopRotateTimer false)
|
||||
(atopacctService false)
|
||||
(netatop false)
|
||||
(atopgpu false)
|
||||
];
|
||||
};
|
||||
netatop = makeTest {
|
||||
name = "atop-netatop";
|
||||
machine = {
|
||||
programs.atop = {
|
||||
enable = true;
|
||||
netatop.enable = true;
|
||||
};
|
||||
};
|
||||
testScript = with assertions; builtins.concatStringsSep "\n" [
|
||||
version
|
||||
(atoprc "")
|
||||
(wrapper false)
|
||||
(atopService true)
|
||||
(atopRotateTimer true)
|
||||
(atopacctService true)
|
||||
(netatop true)
|
||||
(atopgpu false)
|
||||
];
|
||||
};
|
||||
atopgpu = makeTest {
|
||||
name = "atop-atopgpu";
|
||||
machine = {
|
||||
nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (getName pkg) [
|
||||
"cudatoolkit"
|
||||
];
|
||||
|
||||
programs.atop = {
|
||||
enable = true;
|
||||
atopgpu.enable = true;
|
||||
};
|
||||
};
|
||||
testScript = with assertions; builtins.concatStringsSep "\n" [
|
||||
version
|
||||
(atoprc "")
|
||||
(wrapper false)
|
||||
(atopService true)
|
||||
(atopRotateTimer true)
|
||||
(atopacctService true)
|
||||
(netatop false)
|
||||
(atopgpu true)
|
||||
];
|
||||
};
|
||||
everything = makeTest {
|
||||
name = "atop-everthing";
|
||||
machine = {
|
||||
nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (getName pkg) [
|
||||
"cudatoolkit"
|
||||
];
|
||||
|
||||
programs.atop = {
|
||||
enable = true;
|
||||
settings = {
|
||||
flags = "faf1";
|
||||
interval = 2;
|
||||
};
|
||||
setuidWrapper.enable = true;
|
||||
netatop.enable = true;
|
||||
atopgpu.enable = true;
|
||||
};
|
||||
};
|
||||
testScript = with assertions; builtins.concatStringsSep "\n" [
|
||||
version
|
||||
(atoprc "flags faf1\\ninterval 2\\n")
|
||||
(wrapper true)
|
||||
(atopService true)
|
||||
(atopRotateTimer true)
|
||||
(atopacctService true)
|
||||
(netatop true)
|
||||
(atopgpu true)
|
||||
];
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user