utils.diagnostics package¶
Submodules¶
utils.diagnostics.container module¶
SPOT rescue diagnostics, called ONLY by the CI workflows on failure (never
from Ansible rescue: blocks; the lint forbids those).
Captures a full failure snapshot on the current level (all containers with
their logs and inspect, all swarm services with ps and logs, the systemd
journal, host resources), then RECURSES: it copies itself into every running
container that carries python3 and a container runtime (DiD) and repeats the
capture there, pulling each nested snapshot back under
containers/<name>/nested/ - from the outermost caller down to the deepest
runtime, however deep the nesting goes. RESCUE_SEEN carries the ids already
entered, so a runtime that lists itself is cut as a cycle.
Every collector is best-effort: a missing source must never abort the capture.
INFINITO_RESCUE_DIAGNOSTICS_DIR and INFINITO_DNS53_SAMPLER_LOG are
required and never defaulted here, so group_vars/all/05_paths.yml stays the
one source. Prints one summary and ALWAYS exits 1 so a failure stays a failure.
- Usage:
container.py [APP_ID] [CONTEXT]
- utils.diagnostics.container.capture(out: Path, name: str, cmd: list[str], *, timeout: int = 120) None¶
- utils.diagnostics.container.collect_dnsmasq_introspection(out: Path) None¶
fd table and rlimits of every live dnsmasq process.
Run 32118850138 caught dnsmasq alive with zero sockets and nothing in the journal; the socket table alone cannot separate closed listeners from an exhausted fd table or a lost netlink watch, the fd list can.
- utils.diagnostics.container.collect_host(out: Path, app_id: str, context: str, stamp: str) None¶
- utils.diagnostics.container.collect_local_dumps(out: Path) None¶
Copy the in-play role dumps next to the snapshot.
outitself lives under the dump dir (both derive from INFINITO_RESCUE_DIAGNOSTICS_DIR), so the walk must skip its own output subtree or copytree recurses into the growing destination until ENAMETOOLONG.
- utils.diagnostics.container.collect_networks(out: Path, rt: str) None¶
Dump the network definitions behind every container’s resolver.
A container resolves through the runtime’s embedded server on 127.0.0.11, which forwards to whatever the network was created with, so
inspecton the container shows the address but never the forwarder behind it.
- utils.diagnostics.container.collect_resolver_probes(out: Path) None¶
Resolve each probed name, once through the stack and once per nameserver.
getenthonours nsswitch and cannot address a server, so on its own it cannot say which of the resolvers in resolv.conf refused. Where the distro ships a query tool, ask every nameserver directly; where it does not, the per-name verdicts still stand on their own.
- utils.diagnostics.container.collect_runtime(out: Path, rt: str) tuple[list[str], list[str]]¶
- utils.diagnostics.container.collect_service_state(out: Path) None¶
Dump per-service liveness for every loaded service unit.
systemctl list-unitsreports a forking unit as active/running while its guessed main process is gone, and the containers carry nops, so the unit table alone cannot answer whether a daemon is still there. MainPID and NRestarts can.
- utils.diagnostics.container.daemon_json_dns() list[str]¶
Nameservers pinned under the
dnskey of /etc/docker/daemon.json.A Tor node’s resolv.conf names only the local dnsmasq, so probing its entries alone cannot say whether the pinned upstream was still alive when that dnsmasq went silent. Anything unreadable or keyless yields [].
- utils.diagnostics.container.list_lines(cmd: list[str]) list[str]¶
- utils.diagnostics.container.main(argv: list[str]) int¶
- utils.diagnostics.container.recurse(out: Path, rt: str, app_id: str, context: str, depth: int, seen: list[str], stamp: str) int¶
- utils.diagnostics.container.run(cmd: list[str], *, timeout: int = 120, stdin: bytes | None = None) CompletedProcess¶
Best-effort subprocess wrapper: never raises, captures everything.
- utils.diagnostics.container.runtime_bin() str | None¶
- utils.diagnostics.container.sanitize(name: str) str¶
- utils.diagnostics.container.source_name(path: str) str¶
- utils.diagnostics.container.write(path: Path, data: bytes) None¶