STANDARDS.md binds every repository in the NAPALM org: one default branch, Conventional Commits, the device-types contract, test-driven development, clean-code limits, a quality ratchet for existing code, packaging rules and releases by tag to PyPI. napalm-standard holds what a tool can check: the conformance check (with release rules on a tag), the quality ratchet (ported from netOrk's quality_ratchet.py), and the Gitea release from the CHANGELOG section. .gitea/workflows/driver.yml is the CI every driver calls; template/ is a new driver's starting point and passes the check as it is.
46 lines
1.6 KiB
Python
46 lines
1.6 KiB
Python
"""Pure functions: device output in, typed values out. No I/O here."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from typing import TypedDict
|
|
|
|
#: "Key : value" lines of `show version`.
|
|
_FIELD = re.compile(r"^(?P<key>[^:\n]+?)\s*:\s*(?P<value>.*)$", re.MULTILINE)
|
|
#: "3 days, 4:05:06" or "4:05:06".
|
|
_UPTIME = re.compile(r"^(?:(?P<days>\d+) days?, )?(?P<h>\d+):(?P<m>\d\d):(?P<s>\d\d)$")
|
|
_SECONDS_PER_DAY = 86400
|
|
|
|
|
|
class VersionDict(TypedDict):
|
|
model: str
|
|
hostname: str
|
|
os_version: str
|
|
serial_number: str
|
|
uptime: float
|
|
|
|
|
|
def parse_uptime(text: str) -> float:
|
|
"""Seconds from an uptime like ``3 days, 4:05:06``."""
|
|
match = _UPTIME.match(text.strip())
|
|
if not match:
|
|
raise ValueError(f"unreadable uptime: {text!r}")
|
|
days = int(match.group("days") or 0)
|
|
hours, minutes, seconds = (int(match.group(k)) for k in ("h", "m", "s"))
|
|
return float(days * _SECONDS_PER_DAY + hours * 3600 + minutes * 60 + seconds)
|
|
|
|
|
|
def parse_version(text: str) -> VersionDict:
|
|
"""Facts from ``show version``; a field the device did not print is an error."""
|
|
fields = {m.group("key").strip(): m.group("value").strip() for m in _FIELD.finditer(text)}
|
|
try:
|
|
return {
|
|
"model": text.splitlines()[0].removesuffix(" Switch").strip(),
|
|
"hostname": fields["Hostname"],
|
|
"os_version": fields["Firmware"],
|
|
"serial_number": fields["Serial number"],
|
|
"uptime": parse_uptime(fields["Uptime"]),
|
|
}
|
|
except KeyError as exc:
|
|
raise ValueError(f"show version lacks {exc.args[0]}") from exc
|