pdudaemon: init at 1.1.1 (#499230)

This commit is contained in:
Stanisław Pitucha
2026-04-24 13:23:30 +00:00
committed by GitHub
5 changed files with 260 additions and 0 deletions
+1
View File
@@ -692,6 +692,7 @@
./services/hardware/nvidia-optimus.nix
./services/hardware/openrgb.nix
./services/hardware/pcscd.nix
./services/hardware/pdudaemon.nix
./services/hardware/pid-fan-controller.nix
./services/hardware/pommed.nix
./services/hardware/power-profiles-daemon.nix
@@ -0,0 +1,146 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.services.pdudaemon;
configFile = pkgs.writeText "pdudaemon.conf" (
lib.generators.toJSON { } {
daemon = {
hostname = cfg.bindAddress;
port = cfg.port;
logging_level = cfg.logLevel;
listener = cfg.listener;
};
pdus = cfg.pdus;
}
);
in
{
meta = {
maintainers = with lib.maintainers; [
aiyion
emantor
];
};
options = {
services.pdudaemon = {
enable = lib.mkEnableOption "PDUDaemon";
package = lib.mkPackageOption pkgs "pdudaemon" { };
bindAddress = lib.mkOption {
default = "0.0.0.0";
type = lib.types.str;
description = "Bind address for the PDUDaemon.";
};
port = lib.mkOption {
default = 16421;
type = lib.types.port;
description = "Port to bind to.";
};
openFirewall = lib.mkOption {
default = false;
type = lib.types.bool;
description = ''
Whether to automatically open the PDUDaemon listen port in the firewall.
'';
};
listener = lib.mkOption {
default = "http";
type = lib.types.enum [
"http"
"tcp"
];
description = "Which kind of listener to provide.";
};
logLevel = lib.mkOption {
default = "error";
type = lib.types.enum [
"debug"
"info"
"warning"
"error"
];
description = "PDUDaemon log level.";
};
pdus = lib.mkOption {
type = with lib.types; attrsOf anything;
default = { };
description = ''
Structural pdus section of PDUDaemon's pdudaemon.conf.
Refer to <https://github.com/pdudaemon/pdudaemon/blob/main/share/pdudaemon.conf>
for more examples.
'';
example = lib.literalExpression ''
{
cbs350-poe-switch = {
driver = "snmpv1";
community = "private";
oid = ".1.3.6.1.2.1.105.1.1.1.3.1.*;
onsetting = 1;
offsetting = 2;
};
energenie = {
driver = "EG-PMS";
device = "aa:bb:cc:xx:yy";
};
local = {
driver = "localcmdline";
};
};
'';
};
};
};
config = lib.mkIf cfg.enable {
networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [ cfg.port ];
systemd.services.pdudaemon = {
after = [ "network-online.target" ];
description = "Control and Queueing daemon for PDUs";
serviceConfig = {
ExecStart = "${lib.getExe cfg.package} --conf ${configFile}";
Type = "simple";
DynamicUser = "yes";
StateDirectory = "pdudaemon";
ProtectHome = true;
Restart = "on-failure";
CapabilityBoundingSet = "";
PrivateDevices = true;
ProtectClock = true;
ProtectKernelLogs = true;
ProtectControlGroups = true;
ProtectKernelModules = true;
SystemCallArchitectures = "native";
MemoryDenyWriteExecute = true;
RestrictNamespaces = true;
ProtectHostname = true;
LockPersonality = true;
ProtectKernelTunables = true;
RestrictRealtime = true;
ProtectProc = "invisible";
ProcSubset = "pid";
PrivateUsers = true;
SystemCallFilter = [
"@system-service"
"~@privileged"
"~@resources"
];
};
wantedBy = [ "multi-user.target" ];
wants = [ "network-online.target" ];
};
};
}
+1
View File
@@ -1252,6 +1252,7 @@ in
inherit runTest;
};
pdns-recursor = runTest ./pdns-recursor.nix;
pdudaemon = runTest ./pdudaemon.nix;
peerflix = runTest ./peerflix.nix;
peering-manager = runTest ./web-apps/peering-manager.nix;
peertube = handleTestOn [ "x86_64-linux" ] ./web-apps/peertube.nix { };
+50
View File
@@ -0,0 +1,50 @@
{ pkgs, ... }:
{
name = "PDUDaemon";
meta.maintainers = with pkgs.lib.maintainers; [
aiyion
emantor
];
nodes.pdudaemonhost =
{ pkgs, ... }:
{
environment.systemPackages = [ pkgs.curl ];
services.pdudaemon.enable = true;
services.pdudaemon.openFirewall = true;
services.pdudaemon.pdus = {
testpduhost = {
driver = "localcmdline";
cmd_on = "echo '%s on' >> /tmp/pdu";
cmd_off = "echo '%s off' >> /tmp/pdu";
};
};
};
nodes.clienthost =
{ pkgs, ... }:
{
environment.systemPackages = [ pkgs.curl ];
};
testScript =
{ nodes, ... }:
#python
''
with subtest("Wait for pdudaemon startup"):
pdudaemonhost.start()
pdudaemonhost.wait_for_unit("pdudaemon.service")
pdudaemonhost.wait_for_open_port(16421)
print(pdudaemonhost.succeed("curl 'http://localhost:16421/power/control/on?hostname=testpduhost&port=1'"))
with subtest("Connect from client"):
clienthost.start()
clienthost.wait_until_succeeds("curl 'http://pdudaemonhost:16421/power/control/off?hostname=testpduhost&port=1'")
with subtest("Check systemd hardening does not degrade unnoticed"):
exact_threshold = 15
service_name = "pdudaemon"
pdudaemonhost.fail(f"systemd-analyze security {service_name}.service --threshold={exact_threshold-1}")
pdudaemonhost.succeed(f"systemd-analyze security {service_name}.service --threshold={exact_threshold}")
'';
}
+62
View File
@@ -0,0 +1,62 @@
{
lib,
fetchFromGitHub,
python3Packages,
nixosTests,
}:
python3Packages.buildPythonApplication (finalAttrs: {
pname = "pdudaemon";
version = "1.1.1";
pyproject = true;
src = fetchFromGitHub {
owner = "pdudaemon";
repo = "pdudaemon";
tag = finalAttrs.version;
hash = "sha256-YjM1RmsdRfNyxCzK+PmSH8n7ZJ3qeIskTPxu2+EaupQ=";
};
build-system = with python3Packages; [
setuptools
setuptools-scm
];
dependencies = with python3Packages; [
aiohttp
requests
pexpect
systemd-python
paramiko
pyserial
hidapi
pysnmp
pyasn1
pyusb
pymodbus
];
nativeCheckInputs = with python3Packages; [
pytest-asyncio
pytest-mock
pytestCheckHook
];
__structuredAttrs = true;
passthru.tests = {
inherit (nixosTests) pdudaemon;
};
meta = {
changelog = "https://github.com/pdudaemon/pdudaemon/releases/tag/${finalAttrs.src.tag}";
description = "Python Daemon for controlling/sequentially executing commands to PDUs (Power Distribution Units)";
homepage = "https://github.com/pdudaemon/pdudaemon";
license = lib.licenses.gpl2Plus;
maintainers = with lib.maintainers; [
aiyion
emantor
];
mainProgram = "pdudaemon";
};
})