style: ruff format and the lint rules of the driver standard
Mechanical: ruff format, and ruff check --fix with the rule set every NAPALM driver uses (E, F, I, UP, B at line length 100, Python 3.10). What ruff cannot fix comes in the next commit.
This commit is contained in:
1 parent
5786274601
commit
fa10d6617b
6 files changed
+156
-169
No files matched your search
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
@@ -26,8 +25,8 @@ import threading
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
from napalm_device_types import FingerprintRule, FirmwareOfferMixin, SwitchDriver
|
||||
from napalm.base.exceptions import ConnectionException
|
||||
from napalm_device_types import FingerprintRule, FirmwareOfferMixin, SwitchDriver
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Firmware update check — module-level constants and cache
|
||||
@@ -41,9 +40,9 @@ _FW_CDN_BASE = "https://www.downloads.netgear.com/files/GDC"
|
||||
# Verified via HEAD requests against downloads.netgear.com on 2026-05-24.
|
||||
# Update this table when Netgear releases new firmware.
|
||||
_FW_KNOWN_LATEST: dict[str, str] = {
|
||||
"GS305EP": "V2.0.0.10",
|
||||
"GS305EP": "V2.0.0.10",
|
||||
"GS305EPP": "V2.0.0.10",
|
||||
"GS308EP": "V2.0.0.11",
|
||||
"GS308EP": "V2.0.0.11",
|
||||
"GS308EPP": "V2.0.0.11",
|
||||
}
|
||||
|
||||
@@ -63,13 +62,12 @@ def _fw_version_tuple(version: str) -> tuple[int, ...]:
|
||||
|
||||
def _fw_cdn_exists(model: str, version: str) -> bool:
|
||||
"""Return True if a firmware zip exists on the Netgear CDN (HEAD request)."""
|
||||
import urllib.request
|
||||
import ssl
|
||||
import urllib.request
|
||||
|
||||
url = f"{_FW_CDN_BASE}/{model}/{model}_{version}.zip"
|
||||
ctx = ssl.create_default_context()
|
||||
req = urllib.request.Request(url, method="HEAD",
|
||||
headers={"User-Agent": "Mozilla/5.0"})
|
||||
req = urllib.request.Request(url, method="HEAD", headers={"User-Agent": "Mozilla/5.0"})
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=3, context=ctx):
|
||||
return True
|
||||
@@ -150,7 +148,15 @@ class NetgearPlusDriver(FirmwareOfferMixin, SwitchDriver):
|
||||
VENDOR = "Netgear"
|
||||
DRIVER_NAME = "netgear_plus"
|
||||
SNMP_OBJECT_ID_PREFIX = "1.3.6.1.4.1.4526"
|
||||
OUI_PREFIXES = ["00:09:5B", "20:4E:7F", "E0:91:F5", "A0:40:A0", "C4:04:15", "00:1B:2F", "00:1F:33"]
|
||||
OUI_PREFIXES = [
|
||||
"00:09:5B",
|
||||
"20:4E:7F",
|
||||
"E0:91:F5",
|
||||
"A0:40:A0",
|
||||
"C4:04:15",
|
||||
"00:1B:2F",
|
||||
"00:1F:33",
|
||||
]
|
||||
HTTP_FINGERPRINT = [
|
||||
FingerprintRule("netgear gs1", weight=8.0),
|
||||
FingerprintRule("netgear gs2", weight=8.0),
|
||||
@@ -191,8 +197,7 @@ class NetgearPlusDriver(FirmwareOfferMixin, SwitchDriver):
|
||||
from py_netgear_plus import NetgearSwitchConnector
|
||||
except ImportError as exc:
|
||||
raise ConnectionException(
|
||||
"py-netgear-plus is not installed. "
|
||||
"Add 'py-netgear-plus' to your dependencies."
|
||||
"py-netgear-plus is not installed. Add 'py-netgear-plus' to your dependencies."
|
||||
) from exc
|
||||
|
||||
try:
|
||||
@@ -207,9 +212,7 @@ class NetgearPlusDriver(FirmwareOfferMixin, SwitchDriver):
|
||||
except Exception as exc:
|
||||
if isinstance(exc, ConnectionException):
|
||||
raise
|
||||
raise ConnectionException(
|
||||
f"Cannot connect to {self.hostname}: {exc}"
|
||||
) from exc
|
||||
raise ConnectionException(f"Cannot connect to {self.hostname}: {exc}") from exc
|
||||
|
||||
def close(self) -> None:
|
||||
"""Log out from the switch."""
|
||||
@@ -285,9 +288,7 @@ class NetgearPlusDriver(FirmwareOfferMixin, SwitchDriver):
|
||||
hostname = infos.get("switch_name", self.hostname)
|
||||
os_version = infos.get("switch_firmware", "")
|
||||
serial_number = infos.get("switch_serial_number", "")
|
||||
interface_list = [
|
||||
self._port_name(i) for i in range(1, self._connector.ports + 1)
|
||||
]
|
||||
interface_list = [self._port_name(i) for i in range(1, self._connector.ports + 1)]
|
||||
return {
|
||||
"vendor": self.VENDOR,
|
||||
"model": str(model),
|
||||
@@ -313,7 +314,9 @@ class NetgearPlusDriver(FirmwareOfferMixin, SwitchDriver):
|
||||
name = self._port_name(port)
|
||||
status = infos.get(f"port_{port}_status", "off") == "on"
|
||||
speed = float(infos.get(f"port_{port}_connection_speed", 0) or 0)
|
||||
description = port_names.get(port, "") or infos.get(f"port_{port}_description", "") or ""
|
||||
description = (
|
||||
port_names.get(port, "") or infos.get(f"port_{port}_description", "") or ""
|
||||
)
|
||||
interfaces[name] = {
|
||||
"is_up": status,
|
||||
"is_enabled": True,
|
||||
@@ -372,9 +375,7 @@ class NetgearPlusDriver(FirmwareOfferMixin, SwitchDriver):
|
||||
"""
|
||||
infos = self._get_switch_infos()
|
||||
power: dict[str, Any] = {}
|
||||
poe_max_single = getattr(
|
||||
self._connector.switch_model, "POE_MAX_POWER_SINGLE_PORT", None
|
||||
)
|
||||
poe_max_single = getattr(self._connector.switch_model, "POE_MAX_POWER_SINGLE_PORT", None)
|
||||
for port in self._connector.poe_ports:
|
||||
poe_active = infos.get(f"port_{port}_poe_power_active")
|
||||
poe_status = bool(poe_active) if poe_active is not None else False
|
||||
@@ -450,8 +451,8 @@ class NetgearPlusDriver(FirmwareOfferMixin, SwitchDriver):
|
||||
# Example: <span vid-4>...<i ...></i>24</span><span vnm-4 ...>Guest</span>
|
||||
vlan_names: dict[str, str] = {}
|
||||
vlan_entry_re = re.compile(
|
||||
r'<span\s+vid-4[^>]*>.*?(\d+)\s*</span>\s*'
|
||||
r'<span\s+vnm-4[^>]*>([^<]*)</span>',
|
||||
r"<span\s+vid-4[^>]*>.*?(\d+)\s*</span>\s*"
|
||||
r"<span\s+vnm-4[^>]*>([^<]*)</span>",
|
||||
re.DOTALL,
|
||||
)
|
||||
for m in vlan_entry_re.finditer(text):
|
||||
@@ -463,15 +464,14 @@ class NetgearPlusDriver(FirmwareOfferMixin, SwitchDriver):
|
||||
return {}
|
||||
|
||||
vlans: dict[str, Any] = {
|
||||
vid: {"name": name, "tagged": [], "untagged": []}
|
||||
for vid, name in vlan_names.items()
|
||||
vid: {"name": name, "tagged": [], "untagged": []} for vid, name in vlan_names.items()
|
||||
}
|
||||
|
||||
# Parse per-port PVID membership.
|
||||
# Each <span pvid-str ...>1*, 24, 25,</span> corresponds to one port
|
||||
# in order (port 1, port 2, ...). A VID followed by "*" is the native
|
||||
# (untagged) VLAN for that port; all others are tagged.
|
||||
pvid_span_re = re.compile(r'<span\s[^>]*pvid-str[^>]*>([^<]+)</span>')
|
||||
pvid_span_re = re.compile(r"<span\s[^>]*pvid-str[^>]*>([^<]+)</span>")
|
||||
for port_idx, pvid_str in enumerate(pvid_span_re.finditer(text)):
|
||||
port_num = port_idx + 1
|
||||
if port_num > self._connector.ports:
|
||||
@@ -540,34 +540,24 @@ class NetgearPlusDriver(FirmwareOfferMixin, SwitchDriver):
|
||||
return {"running": "", "startup": "", "candidate": ""}
|
||||
|
||||
def load_merge_candidate(self, filename: str | None = None, config: str | None = None) -> None:
|
||||
raise NotImplementedError(
|
||||
"Config management is not supported for Netgear Plus switches."
|
||||
)
|
||||
raise NotImplementedError("Config management is not supported for Netgear Plus switches.")
|
||||
|
||||
def load_replace_candidate(self, filename: str | None = None, config: str | None = None) -> None:
|
||||
raise NotImplementedError(
|
||||
"Config management is not supported for Netgear Plus switches."
|
||||
)
|
||||
def load_replace_candidate(
|
||||
self, filename: str | None = None, config: str | None = None
|
||||
) -> None:
|
||||
raise NotImplementedError("Config management is not supported for Netgear Plus switches.")
|
||||
|
||||
def compare_config(self) -> str:
|
||||
raise NotImplementedError(
|
||||
"Config management is not supported for Netgear Plus switches."
|
||||
)
|
||||
raise NotImplementedError("Config management is not supported for Netgear Plus switches.")
|
||||
|
||||
def commit_config(self, message: str = "") -> None:
|
||||
raise NotImplementedError(
|
||||
"Config management is not supported for Netgear Plus switches."
|
||||
)
|
||||
raise NotImplementedError("Config management is not supported for Netgear Plus switches.")
|
||||
|
||||
def discard_config(self) -> None:
|
||||
raise NotImplementedError(
|
||||
"Config management is not supported for Netgear Plus switches."
|
||||
)
|
||||
raise NotImplementedError("Config management is not supported for Netgear Plus switches.")
|
||||
|
||||
def rollback(self) -> None:
|
||||
raise NotImplementedError(
|
||||
"Config management is not supported for Netgear Plus switches."
|
||||
)
|
||||
raise NotImplementedError("Config management is not supported for Netgear Plus switches.")
|
||||
|
||||
def has_pending_commit(self) -> bool:
|
||||
return False
|
||||
@@ -587,9 +577,7 @@ class NetgearPlusDriver(FirmwareOfferMixin, SwitchDriver):
|
||||
|
||||
Not supported on Netgear Plus switches via HTTP.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"ping() is not supported for Netgear Plus switches."
|
||||
)
|
||||
raise NotImplementedError("ping() is not supported for Netgear Plus switches.")
|
||||
|
||||
def traceroute(
|
||||
self,
|
||||
@@ -603,9 +591,7 @@ class NetgearPlusDriver(FirmwareOfferMixin, SwitchDriver):
|
||||
|
||||
Not supported on Netgear Plus switches via HTTP.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"traceroute() is not supported for Netgear Plus switches."
|
||||
)
|
||||
raise NotImplementedError("traceroute() is not supported for Netgear Plus switches.")
|
||||
|
||||
def cli(self, commands: list[str], encoding: str = "text") -> dict[str, Any]:
|
||||
"""Send CLI commands.
|
||||
@@ -689,12 +675,8 @@ class NetgearPlusDriver(FirmwareOfferMixin, SwitchDriver):
|
||||
Reports PoE power status from switch infos.
|
||||
"""
|
||||
infos = self._get_switch_infos()
|
||||
poe_max_all = getattr(
|
||||
self._connector.switch_model, "POE_MAX_POWER_ALL_PORTS", None
|
||||
)
|
||||
poe_max_single = getattr(
|
||||
self._connector.switch_model, "POE_MAX_POWER_SINGLE_PORT", None
|
||||
)
|
||||
poe_max_all = getattr(self._connector.switch_model, "POE_MAX_POWER_ALL_PORTS", None)
|
||||
poe_max_single = getattr(self._connector.switch_model, "POE_MAX_POWER_SINGLE_PORT", None)
|
||||
ports: dict[str, Any] = {}
|
||||
for p in self._connector.poe_ports:
|
||||
poe_active = infos.get(f"port_{p}_poe_power_active")
|
||||
@@ -742,9 +724,7 @@ class NetgearPlusDriver(FirmwareOfferMixin, SwitchDriver):
|
||||
|
||||
Not supported via HTTP API.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"set_interface() is not supported for Netgear Plus switches."
|
||||
)
|
||||
raise NotImplementedError("set_interface() is not supported for Netgear Plus switches.")
|
||||
|
||||
# ── SNMP / Health ──────────────────────────────────────────────────────────
|
||||
|
||||
@@ -763,9 +743,10 @@ class NetgearPlusDriver(FirmwareOfferMixin, SwitchDriver):
|
||||
decide (napalm-device-types ``FirmwareOfferMixin``).
|
||||
"""
|
||||
try:
|
||||
model_name: str = getattr(
|
||||
self._connector.switch_model, "MODEL_NAME", ""
|
||||
) or self._connector.switch_model.__class__.__name__
|
||||
model_name: str = (
|
||||
getattr(self._connector.switch_model, "MODEL_NAME", "")
|
||||
or self._connector.switch_model.__class__.__name__
|
||||
)
|
||||
current_fw: str = self._get_switch_infos().get("switch_firmware", "")
|
||||
latest_fw = _fw_get_latest(model_name) if model_name and current_fw else None
|
||||
if latest_fw is None:
|
||||
|
||||
Reference in new issue
Block a user