feat: report where an update comes from and whether it is a security fix, refresh the index, read the host status
For netOrk MVP 5, on napalm-device-types 2.3.0:
- get_available_updates reads `apt list --upgradable` over the exec path
(APT_UPGRADABLE_COMMAND), so each update carries its suite and security
status; the APT API, which names only "Debian"/"Proxmox", is the fallback
with security unknown. It raises when neither answers instead of returning
[] -- it used to swallow every error.
- refresh_available_updates(): POST nodes/{n}/apt/update.
- HostStatusMixin over the exec path (reboot required, self-patching).
This commit is contained in:
@@ -20,6 +20,8 @@ import logging
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
from napalm_device_types import APT_UPGRADABLE_COMMAND, parse_apt_upgradable
|
||||
|
||||
from napalm_proxmox import utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -362,22 +364,47 @@ class ProxmoxSystemMixin:
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
def get_available_updates(self) -> list[_JsonDict]:
|
||||
"""Return list of upgradable packages from the Proxmox APT API."""
|
||||
updates: list[_JsonDict] = []
|
||||
"""Return the node's upgradable packages, with origin and security status.
|
||||
|
||||
``apt list --upgradable`` over the exec path names each candidate's suite
|
||||
(``trixie-security``); the APT API names only an Origin ("Debian",
|
||||
"Proxmox") and is the fallback, with the security status unknown.
|
||||
|
||||
:raises Exception: when neither answers -- never an empty list for
|
||||
"could not read".
|
||||
"""
|
||||
try:
|
||||
for upd in self._api.nodes(self._node_name).apt.update.get():
|
||||
pkg = upd.get("Package", "")
|
||||
if not pkg:
|
||||
continue
|
||||
updates.append({
|
||||
"name": pkg,
|
||||
"current_version": upd.get("OldVersion", ""),
|
||||
"new_version": upd.get("Version", ""),
|
||||
})
|
||||
except Exception as exc:
|
||||
logger.debug("Failed to fetch available updates: %s", exc)
|
||||
updates = parse_apt_upgradable(self._exec_ssh_command(APT_UPGRADABLE_COMMAND) or "")
|
||||
except ValueError as exc:
|
||||
logger.debug("apt list over the exec path failed, using the API: %s", exc)
|
||||
updates = self._updates_from_api()
|
||||
return sorted(updates, key=lambda u: u["name"])
|
||||
|
||||
def _updates_from_api(self) -> list[_JsonDict]:
|
||||
return [
|
||||
{
|
||||
"name": upd["Package"],
|
||||
"current_version": upd.get("OldVersion", ""),
|
||||
"new_version": upd.get("Version", ""),
|
||||
"origin": upd.get("Origin"),
|
||||
"security": None,
|
||||
}
|
||||
for upd in self._api.nodes(self._node_name).apt.update.get() # type: ignore[union-attr]
|
||||
if upd.get("Package")
|
||||
]
|
||||
|
||||
def refresh_available_updates(self) -> _JsonDict:
|
||||
"""Resynchronise the node's package index (``POST nodes/{n}/apt/update``)."""
|
||||
try:
|
||||
task = self._api.nodes(self._node_name).apt.update.post() # type: ignore[union-attr]
|
||||
except Exception as exc:
|
||||
return {"success": False, "output": str(exc)}
|
||||
return {"success": True, "output": f"Package index refresh started ({task})"}
|
||||
|
||||
def _run_host_status_command(self, command: str) -> str:
|
||||
"""The transport for ``HostStatusMixin.get_host_status``: the exec path."""
|
||||
return str(self._exec_ssh_command(command))
|
||||
|
||||
def apply_updates(self, packages: list[str]) -> _JsonDict:
|
||||
"""Upgrade the given packages via ``apt-get install`` over SSH."""
|
||||
for pkg in packages:
|
||||
|
||||
Reference in New Issue
Block a user