feat: report the configured package proxy, not apt_proxy_missing (0.9.0) #32
No files matched your search
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- **Breaking:** `get_device_warnings` is gone, and with it the `apt_proxy_url` argument.
|
||||||
|
The driver reports facts and the application names warnings (napalm-device-types 4.14):
|
||||||
|
`get_package_proxy()` says which proxy apt fetches through; pending updates were always
|
||||||
|
in `get_available_updates()`. Requires napalm-device-types 4.14.0; version 0.9.0.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- **Breaking:** `run_device_action` is gone, with `fix_snmp`, `fix_apt_proxy`,
|
- **Breaking:** `run_device_action` is gone, with `fix_snmp`, `fix_apt_proxy`,
|
||||||
|
|||||||
+7
-34
@@ -346,8 +346,6 @@ class LinuxDriver(
|
|||||||
# Optional sudo password for privilege escalation (e.g. apt-get update)
|
# Optional sudo password for privilege escalation (e.g. apt-get update)
|
||||||
self._sudo_password: str | None = optional_args.get("sudo_password")
|
self._sudo_password: str | None = optional_args.get("sudo_password")
|
||||||
# Expected apt proxy URL — checked as a device warning on apt systems.
|
# Expected apt proxy URL — checked as a device warning on apt systems.
|
||||||
# Only set when apt_proxy_enabled=true in NetOrk settings; empty string disables the check.
|
|
||||||
self._apt_proxy_url: str = optional_args.get("apt_proxy_url", "")
|
|
||||||
|
|
||||||
if optional_args.get("debugging"):
|
if optional_args.get("debugging"):
|
||||||
logger.setLevel(logging.DEBUG)
|
logger.setLevel(logging.DEBUG)
|
||||||
@@ -1510,38 +1508,13 @@ class LinuxDriver(
|
|||||||
f"Package manager '{self._pkg_manager}' is not supported"
|
f"Package manager '{self._pkg_manager}' is not supported"
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_device_warnings(self) -> List[dict[str, Any]]:
|
def get_package_proxy(self) -> str | None:
|
||||||
"""Return warning dicts for issues detected on this device.
|
"""The proxy apt fetches through (``00proxy``); None for none or a non-apt host."""
|
||||||
|
if self._pkg_manager != "apt":
|
||||||
Currently detects:
|
return None
|
||||||
- package updates available (uses local package cache)
|
conf = self._send(f"cat {_APT_PROXY_CONF} 2>/dev/null || true")
|
||||||
- apt proxy not configured (apt systems only)
|
found = re.search(r'Acquire::http::Proxy\s+"([^"]+)"', conf)
|
||||||
"""
|
return found.group(1) if found else None
|
||||||
warnings: List[dict[str, Any]] = []
|
|
||||||
try:
|
|
||||||
updates = self.get_available_updates()
|
|
||||||
except Exception as exc:
|
|
||||||
logger.warning("get_device_warnings: get_available_updates() failed: %s", exc)
|
|
||||||
updates = []
|
|
||||||
if updates:
|
|
||||||
warnings.append({
|
|
||||||
"code": "updates_available",
|
|
||||||
"meta": {
|
|
||||||
"count": len(updates),
|
|
||||||
"packages": [u.get("name", "") for u in updates],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if self._pkg_manager == "apt" and self._apt_proxy_url:
|
|
||||||
try:
|
|
||||||
current = self._send("cat /etc/apt/apt.conf.d/00proxy 2>/dev/null || true").strip()
|
|
||||||
if self._apt_proxy_url not in current:
|
|
||||||
warnings.append({
|
|
||||||
"code": "apt_proxy_missing",
|
|
||||||
"meta": {"expected_url": self._apt_proxy_url},
|
|
||||||
})
|
|
||||||
except Exception as exc:
|
|
||||||
logger.warning("get_device_warnings: apt proxy check failed: %s", exc)
|
|
||||||
return warnings
|
|
||||||
|
|
||||||
def _get_updates_apt(self) -> list[UpdateDict]:
|
def _get_updates_apt(self) -> list[UpdateDict]:
|
||||||
# apt list --upgradable does not need root; avoid sudo so it works even
|
# apt list --upgradable does not need root; avoid sudo so it works even
|
||||||
|
|||||||
+2
-2
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "napalm-linux"
|
name = "napalm-linux"
|
||||||
version = "0.8.0"
|
version = "0.9.0"
|
||||||
description = "NAPALM driver for generic Linux systems via SSH"
|
description = "NAPALM driver for generic Linux systems via SSH"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.9"
|
requires-python = ">=3.9"
|
||||||
@@ -37,7 +37,7 @@ classifiers = [
|
|||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"napalm>=4.0",
|
"napalm>=4.0",
|
||||||
"napalm-device-types>=4.13.0",
|
"napalm-device-types>=4.14.0",
|
||||||
"netmiko>=4.0.0",
|
"netmiko>=4.0.0",
|
||||||
"paramiko>=5.0.0", # CVE-2026-44405
|
"paramiko>=5.0.0", # CVE-2026-44405
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -211,3 +211,26 @@ class TestManagementPeer:
|
|||||||
@pytest.mark.parametrize("answer", ["", "\n", "garbage", "not-an-ip 1 2 3"])
|
@pytest.mark.parametrize("answer", ["", "\n", "garbage", "not-an-ip 1 2 3"])
|
||||||
def test_nothing_usable_is_none(self, answer):
|
def test_nothing_usable_is_none(self, answer):
|
||||||
assert self._driver(answer).get_management_peer() is None
|
assert self._driver(answer).get_management_peer() is None
|
||||||
|
|
||||||
|
|
||||||
|
class TestFactsNotWarnings:
|
||||||
|
"""The driver reports facts; the application names warnings (#28, netOrk #1111)."""
|
||||||
|
|
||||||
|
def test_no_warnings_of_its_own(self):
|
||||||
|
assert "get_device_warnings" not in vars(LinuxDriver)
|
||||||
|
|
||||||
|
def test_the_package_proxy_apt_fetches_through(self, driver):
|
||||||
|
driver._send = lambda command, read_timeout=100: 'Acquire::http::Proxy "http://c:3142";'
|
||||||
|
assert driver.get_package_proxy() == "http://c:3142"
|
||||||
|
|
||||||
|
def test_no_proxy_set(self, driver):
|
||||||
|
driver._send = lambda command, read_timeout=100: ""
|
||||||
|
assert driver.get_package_proxy() is None
|
||||||
|
|
||||||
|
def test_not_an_apt_host(self, driver):
|
||||||
|
driver._pkg_manager = "dnf"
|
||||||
|
assert driver.get_package_proxy() is None
|
||||||
|
|
||||||
|
def test_the_applications_proxy_url_is_no_argument_any_more(self):
|
||||||
|
d = LinuxDriver("h", "admin", "p", optional_args={"apt_proxy_url": "http://c:3142"})
|
||||||
|
assert not hasattr(d, "_apt_proxy_url")
|
||||||
@@ -123,7 +123,7 @@ def _driver(host: Host) -> LinuxDriver:
|
|||||||
"h",
|
"h",
|
||||||
"admin",
|
"admin",
|
||||||
"p",
|
"p",
|
||||||
optional_args={"sudo_password": _PW, "apt_proxy_url": "http://proxy:3142"},
|
optional_args={"sudo_password": _PW},
|
||||||
)
|
)
|
||||||
driver._device = MagicMock()
|
driver._device = MagicMock()
|
||||||
driver._device.send_command.side_effect = _shell
|
driver._device.send_command.side_effect = _shell
|
||||||
|
|||||||
Reference in new issue
Block a user