fix(linux): fix_snmp installs snmpd without refreshing apt cache first

A fresh cloud image's apt cache is stale/effectively empty — installing
snmpd without an apt-get update first could fail outright or hang on
unreachable mirrors, and the install call wasn't guarded, so a timeout
propagated as an opaque unguarded exception instead of a clean failure
result.

Also adds a new apt_update_upgrade device action (apt-get update +
upgrade), used by netork's VM-provisioning bootstrap alongside the
existing fix_apt_proxy action to fully prep a freshly provisioned VM's
apt before installing anything on it.
This commit is contained in:
Christian Manivong
2026-07-08 17:09:19 +02:00
parent 3ca2ed72ed
commit 8436013dcd
2 changed files with 190 additions and 2 deletions
+51 -2
View File
@@ -1884,8 +1884,43 @@ class LinuxDriver(OSDriver):
return self._action_fix_snmp()
if action == "fix_apt_proxy":
return self._action_fix_apt_proxy()
if action == "apt_update_upgrade":
return self._action_apt_update_upgrade()
raise NotImplementedError(f"Unknown action: {action!r}")
def _action_apt_update_upgrade(self) -> DeviceActionResultDict:
"""Refresh the apt cache and upgrade all packages (apt-based systems only)."""
if self._pkg_manager != "apt":
return {
"success": True,
"output": f"Skipped — package manager is {self._pkg_manager!r}, not apt.",
}
sudo_check = self._send("sudo -n true 2>&1 || echo __SUDO_NEEDS_PW__")
if "__SUDO_NEEDS_PW__" in sudo_check or "password is required" in sudo_check.lower():
if not self._sudo_password:
return {
"success": False,
"output": (
"sudo requires a password on this device but none is configured in "
"netOrk. Please add the sudo password to a Credential Profile assigned "
"to this device, or configure passwordless sudo (NOPASSWD) for this user."
),
}
lines: list[str] = []
try:
out = self._sudo("apt-get update -y 2>&1", read_timeout=90)
lines.append(f"[update] {out.strip()[-300:]}")
out = self._sudo(
"DEBIAN_FRONTEND=noninteractive apt-get upgrade -y 2>&1", read_timeout=240
)
lines.append(f"[upgrade] {out.strip()[-300:]}")
return {"success": True, "output": "\n".join(lines)}
except Exception as exc:
lines.append(f"[error] {exc}")
return {"success": False, "output": "\n".join(lines)}
def _action_fix_snmp(self) -> DeviceActionResultDict:
"""Install, configure and start snmpd with community 'public'."""
lines: list[str] = []
@@ -1908,6 +1943,16 @@ class LinuxDriver(OSDriver):
if not pkg_mgr:
return {"success": False, "output": "Package manager not detected — cannot install snmpd."}
# Refresh the package index first — a freshly provisioned (or simply
# long-untouched) system's cache can be stale/empty, which makes the
# install below fail outright rather than just being slow.
if pkg_mgr == "apt":
try:
update_out = self._sudo("apt-get update -y 2>&1", read_timeout=90)
lines.append(f"[update] {update_out.strip()[-200:]}")
except Exception as exc:
lines.append(f"[warn] apt-get update failed: {exc}")
# Install both snmpd (daemon) and snmp (client tools incl. snmpget for probing)
install_cmd: dict[str, str] = {
"apt": "DEBIAN_FRONTEND=noninteractive apt-get install -y snmpd snmp 2>&1",
@@ -1918,8 +1963,12 @@ class LinuxDriver(OSDriver):
}
cmd = install_cmd.get(pkg_mgr)
if cmd:
out = self._sudo(cmd, read_timeout=120)
lines.append(f"[install] {out.strip()[-200:]}")
try:
out = self._sudo(cmd, read_timeout=120)
lines.append(f"[install] {out.strip()[-200:]}")
except Exception as exc:
lines.append(f"[error] install failed: {exc}")
return {"success": False, "output": "\n".join(lines)}
# 2. Determine the IP netOrk is connecting from by checking the established SSH connection
netork_ip = ""