86 lines
2.6 KiB
Python
Executable File
86 lines
2.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Read-only environment report used before installing the persistent node."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import platform
|
|
import shutil
|
|
import subprocess
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
def command(argv: list[str], timeout: int = 20) -> dict[str, Any]:
|
|
if shutil.which(argv[0]) is None:
|
|
return {"available": False}
|
|
try:
|
|
result = subprocess.run(
|
|
argv,
|
|
stdin=subprocess.DEVNULL,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT,
|
|
text=True,
|
|
timeout=timeout,
|
|
shell=False,
|
|
)
|
|
return {
|
|
"available": True,
|
|
"exit_code": result.returncode,
|
|
"output": result.stdout[:32_000],
|
|
}
|
|
except subprocess.TimeoutExpired:
|
|
return {"available": True, "error": "timeout"}
|
|
|
|
|
|
def inspect() -> dict[str, Any]:
|
|
efi = command(["efibootmgr", "-v"])
|
|
disks = command(["lsblk", "-J", "-o", "NAME,FSTYPE,LABEL,MOUNTPOINTS"])
|
|
virt = command(["systemd-detect-virt"])
|
|
boot_text = str(efi.get("output", "")).lower()
|
|
disk_text = str(disks.get("output", "")).lower()
|
|
windows_hint = any(
|
|
marker in boot_text or marker in disk_text
|
|
for marker in ("windows boot manager", "microsoft", "ntfs", "bitlocker")
|
|
)
|
|
return {
|
|
"schema": "canger-node-preflight/v1",
|
|
"hostname": platform.node(),
|
|
"platform": platform.platform(),
|
|
"machine": platform.machine(),
|
|
"systemd_running": os.path.isdir("/run/systemd/system"),
|
|
"virtualization": virt,
|
|
"efi_boot_entries": efi,
|
|
"block_filesystems": disks,
|
|
"possible_windows_dual_boot": windows_hint,
|
|
"continuity": {
|
|
"while_linux_is_booted": "supported",
|
|
"after_reboot_back_into_linux": "supported",
|
|
"while_another_dual_boot_os_is_active": "not_possible_from_linux",
|
|
},
|
|
"note": (
|
|
"A dual-boot switch powers off Linux. Cross-OS continuity requires "
|
|
"a companion agent in the other OS, a virtual machine, or separate hardware."
|
|
),
|
|
}
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--output")
|
|
args = parser.parse_args()
|
|
report = inspect()
|
|
text = json.dumps(report, ensure_ascii=False, indent=2) + "\n"
|
|
if args.output:
|
|
target = Path(args.output)
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
target.write_text(text, encoding="utf-8")
|
|
os.chmod(target, 0o600)
|
|
print(text, end="")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|