Merge pull request #123053 from pschyska/master

atop, netatop, nixos/atop: improve packaging and options
This commit is contained in:
Michael Raskin
2021-05-18 10:54:13 +00:00
committed by GitHub
10 changed files with 475 additions and 29 deletions
+118 -8
View File
@@ -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"; }; };
}
);
}
+1
View File
@@ -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 { };
+213
View File
@@ -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)
];
};
}