feat: standards, shared CI and release tooling for NAPALM drivers
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.
This commit is contained in:
commit
db6b2e4ea7
37 files changed
+2894
No files matched your search
@@ -0,0 +1,5 @@
|
||||
Example 24 Switch
|
||||
Hostname : sw-lab-01
|
||||
Firmware : 1.0.0
|
||||
Serial number : EX0000000001
|
||||
Uptime : 3 days, 4:05:06
|
||||
@@ -0,0 +1,66 @@
|
||||
"""The driver with its transport replaced: what it sends, what it returns."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
|
||||
import pytest
|
||||
from napalm.base.exceptions import ConnectionException
|
||||
|
||||
from napalm_example import ExampleDriver
|
||||
|
||||
FIXTURES = pathlib.Path(__file__).parent / "fixtures"
|
||||
|
||||
|
||||
class FakeTransport:
|
||||
def __init__(self, answers: dict[str, str]) -> None:
|
||||
self.answers = answers
|
||||
self.sent: list[str] = []
|
||||
self.closed = False
|
||||
|
||||
def command(self, line: str) -> str:
|
||||
self.sent.append(line)
|
||||
return self.answers[line]
|
||||
|
||||
def close(self) -> None:
|
||||
self.closed = True
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def driver() -> ExampleDriver:
|
||||
return ExampleDriver("192.0.2.10", "admin", "secret")
|
||||
|
||||
|
||||
def test_get_facts(driver: ExampleDriver) -> None:
|
||||
show_version = (FIXTURES / "example24-1.0.0-show-version.txt").read_text()
|
||||
driver.transport = FakeTransport({"show version": show_version})
|
||||
|
||||
facts = driver.get_facts()
|
||||
|
||||
assert facts["vendor"] == "Example"
|
||||
assert facts["hostname"] == "sw-lab-01"
|
||||
assert facts["fqdn"] == "sw-lab-01"
|
||||
assert facts["interface_list"] == []
|
||||
assert facts["uptime"] == 273906.0
|
||||
|
||||
|
||||
def test_a_getter_before_open_raises(driver: ExampleDriver) -> None:
|
||||
with pytest.raises(ConnectionException):
|
||||
driver.get_facts()
|
||||
|
||||
|
||||
def test_close_closes_the_transport_once(driver: ExampleDriver) -> None:
|
||||
transport = FakeTransport({})
|
||||
driver.transport = transport
|
||||
|
||||
driver.close()
|
||||
driver.close()
|
||||
|
||||
assert transport.closed
|
||||
assert driver.transport is None
|
||||
|
||||
|
||||
def test_the_driver_is_registered_under_its_name() -> None:
|
||||
from napalm import get_network_driver
|
||||
|
||||
assert get_network_driver("example") is ExampleDriver
|
||||
@@ -0,0 +1,41 @@
|
||||
"""Parsers against recorded device output."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
|
||||
import pytest
|
||||
|
||||
from napalm_example.parsers import parse_uptime, parse_version
|
||||
|
||||
FIXTURES = pathlib.Path(__file__).parent / "fixtures"
|
||||
|
||||
|
||||
def test_version_output_becomes_facts() -> None:
|
||||
text = (FIXTURES / "example24-1.0.0-show-version.txt").read_text()
|
||||
|
||||
assert parse_version(text) == {
|
||||
"model": "Example 24",
|
||||
"hostname": "sw-lab-01",
|
||||
"os_version": "1.0.0",
|
||||
"serial_number": "EX0000000001",
|
||||
"uptime": 273906.0,
|
||||
}
|
||||
|
||||
|
||||
def test_a_missing_field_is_an_error_not_an_empty_value() -> None:
|
||||
with pytest.raises(ValueError, match="Serial number"):
|
||||
parse_version("Example 24 Switch\nHostname : a\nFirmware : 1\nUptime : 0:00:01\n")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("text", "seconds"),
|
||||
[("0:00:01", 1.0), ("1 day, 0:00:00", 86400.0), ("3 days, 4:05:06", 273906.0)],
|
||||
)
|
||||
def test_uptime(text: str, seconds: float) -> None:
|
||||
assert parse_uptime(text) == seconds
|
||||
|
||||
|
||||
def test_an_unreadable_uptime_is_an_error() -> None:
|
||||
with pytest.raises(ValueError, match="uptime"):
|
||||
parse_uptime("soon")
|
||||
Reference in new issue
Block a user