163 lines
5.5 KiB
Nix
163 lines
5.5 KiB
Nix
{ ... }:
|
|
{
|
|
name = "rwhod";
|
|
|
|
nodes = {
|
|
host1 = { pkgs, ... }: {
|
|
imports = [ ../module.nix ];
|
|
|
|
boot.kernelParams = [ "audit=1" ];
|
|
|
|
environment.systemPackages = [ pkgs.screen ];
|
|
|
|
services.roowho2 = {
|
|
enable = true;
|
|
settings = {
|
|
log_level = "debug";
|
|
rwhod = {
|
|
enable = true;
|
|
# Effectively infinite interval means host1 will never announce itself
|
|
# to the network in a periodic broadcast. We will force it to do so
|
|
# via the realtime push mechanism when a user logs in or out.
|
|
send_interval_seconds = 99999999;
|
|
};
|
|
fingerd.enable = false;
|
|
};
|
|
};
|
|
|
|
users.users.alice.isNormalUser = true;
|
|
};
|
|
|
|
host2 = { pkgs, ... }: {
|
|
imports = [ ../module.nix ];
|
|
|
|
environment.systemPackages = [ pkgs.screen ];
|
|
|
|
services.roowho2 = {
|
|
enable = true;
|
|
settings = {
|
|
log_level = "debug";
|
|
rwhod = {
|
|
enable = true;
|
|
send_interval_seconds = 5;
|
|
};
|
|
fingerd.enable = false;
|
|
};
|
|
};
|
|
|
|
users.users.bob.isNormalUser = true;
|
|
|
|
specialisation.ignore-bob.configuration = {
|
|
services.roowho2.settings.rwhod.ignoreUsers = [ "bob" ];
|
|
};
|
|
};
|
|
|
|
host3 = { ... }: {
|
|
imports = [ ../module.nix ];
|
|
|
|
services.roowho2 = {
|
|
enable = true;
|
|
settings = {
|
|
log_level = "debug";
|
|
rwhod = {
|
|
enable = true;
|
|
send_interval_seconds = 5;
|
|
# Without any interfaces to broadcast on,
|
|
# this host will never announce itself to the network.
|
|
interfaces = [ ];
|
|
};
|
|
fingerd.enable = false;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
testScript = ''
|
|
import json
|
|
|
|
def rwho_entries(machine):
|
|
return json.loads(machine.succeed("rwho --all --json"))
|
|
|
|
def ruptime_entries(machine):
|
|
return json.loads(machine.succeed("ruptime --all --json"))
|
|
|
|
def find_ruptime_entry(entries, hostname):
|
|
return next((e for e in entries if e["hostname"] == hostname), None)
|
|
|
|
def find_rwho_user(entries, hostname, username):
|
|
return next(
|
|
(user for user in entries.get(hostname, []) if user["user_id"] == username),
|
|
None,
|
|
)
|
|
|
|
def wait_for_host_in_ruptime(machine, hostname, present=True, timeout=300):
|
|
def check(_last_try):
|
|
found = find_ruptime_entry(ruptime_entries(machine), hostname) is not None
|
|
return found == present
|
|
retry(check, timeout_seconds=timeout)
|
|
|
|
def wait_for_user_in_rwho(machine, hostname, username, present=True, timeout=300):
|
|
def check(_last_try):
|
|
found = find_rwho_user(rwho_entries(machine), hostname, username) is not None
|
|
return found == present
|
|
retry(check, timeout_seconds=timeout)
|
|
|
|
def start_session(machine, username, session_name):
|
|
machine.succeed(f"screen -dmS {session_name} login -f {username}")
|
|
|
|
def stop_session(machine, session_name):
|
|
machine.succeed(f"screen -S {session_name} -X quit")
|
|
|
|
start_all()
|
|
for machine in [host1, host2, host3]:
|
|
machine.wait_for_unit("roowho2-rwhod.socket")
|
|
machine.wait_for_unit("roowho2-client.socket")
|
|
machine.wait_for_unit("multi-user.target")
|
|
machine.succeed("ruptime --all --json")
|
|
|
|
with subtest("host2 broadcasts its status periodically"):
|
|
host2.wait_for_console_text(r"Sending rwhod packet to interface")
|
|
wait_for_host_in_ruptime(host1, "host2", timeout=20)
|
|
wait_for_host_in_ruptime(host3, "host2", timeout=20)
|
|
|
|
with subtest("Check host2 ruptime fields"):
|
|
entry = find_ruptime_entry(ruptime_entries(host1), "host2")
|
|
assert entry is not None, "Expected host2 to be present in host1's ruptime"
|
|
assert entry["users"] == [], f"Expected no users logged in yet, got: {entry!r}"
|
|
|
|
with subtest("Starting a session on host1 triggers a realtime status push"):
|
|
start_session(host1, "alice", "alice-session")
|
|
wait_for_user_in_rwho(host2, "host1", "alice")
|
|
wait_for_user_in_rwho(host3, "host1", "alice")
|
|
|
|
with subtest("Stopping a session on host1 triggers a realtime status push"):
|
|
stop_session(host1, "alice-session")
|
|
wait_for_user_in_rwho(host2, "host1", "alice", present=False)
|
|
wait_for_user_in_rwho(host3, "host1", "alice", present=False)
|
|
|
|
with subtest("Starting a session on host2 eventually becomes visible"):
|
|
start_session(host2, "bob", "bob-session")
|
|
wait_for_user_in_rwho(host1, "host2", "bob")
|
|
wait_for_user_in_rwho(host3, "host2", "bob")
|
|
|
|
with subtest("Switching host2 to ignore bob removes them from the next broadcast"):
|
|
host2.succeed(
|
|
"/run/current-system/specialisation/ignore-bob/bin/switch-to-configuration test"
|
|
)
|
|
host2.wait_for_unit("roowho2-client.socket")
|
|
host2.succeed("who | grep bob") # Ensure bob is still logged in
|
|
wait_for_user_in_rwho(host1, "host2", "bob", present=False)
|
|
wait_for_user_in_rwho(host3, "host2", "bob", present=False)
|
|
|
|
with subtest("host3's interfaces allowlist keeps it from ever broadcasting"):
|
|
assert find_ruptime_entry(ruptime_entries(host1), "host3") is None, (
|
|
"host1 should not have learned about host3"
|
|
)
|
|
assert find_ruptime_entry(ruptime_entries(host2), "host3") is None, (
|
|
"host2 should not have learned about host3"
|
|
)
|
|
wait_for_host_in_ruptime(host3, "host1")
|
|
wait_for_host_in_ruptime(host3, "host2")
|
|
'';
|
|
}
|