# services/net_info.py import subprocess import json def _run(cmd: list[str]) -> str: return subprocess.check_output(cmd, text=True).strip() def _first_ipv4_cidr_from_ip_json(ip_json: dict) -> str | None: # Find first IPv4 on the interface for addr in ip_json.get("addr_info", []): if addr.get("family") == "inet" and addr.get("local") and addr.get("prefixlen") is not None: return f'{addr["local"]}/{addr["prefixlen"]}' return None def get_iface_cidr(iface: str) -> str | None: # ip -j addr show dev eth0 raw = _run(["ip", "-j", "addr", "show", "dev", iface]) data = json.loads(raw) if not data: return None return _first_ipv4_cidr_from_ip_json(data[0]) def get_default_gw_for_iface(iface: str) -> str | None: lines = _run(["ip", "route", "show", "default", "dev", iface]).splitlines() for line in lines: parts = line.split() if parts and parts[0] == "default": try: idx = parts.index("via") return parts[idx + 1] except ValueError: continue except IndexError: continue return None def get_eth0_dhcp_snapshot() -> dict: iface = "eth0" cidr = get_iface_cidr(iface) or "" gw = get_default_gw_for_iface(iface) or "" if not cidr or not gw: raise RuntimeError("Could not determine eth0 CIDR and/or default gateway") return {"iface": iface, "cidr": cidr, "gw": gw}