fix: inherit PhoneDriver, and stop asserting a contract the driver rejected

Closes netork#117.

A Yealink is a desk phone. It inherited AccessPointDriver because nothing better
existed, and netOrk reads that role to populate its wireless page, its AP
profile pickers and its SSID drift view — so a phone appeared in all three.
napalm-device-types now has PhoneDriver. TYPE_LABEL comes from it, so the local
override is gone.

The two failing fingerprint tests were asserting the opposite of a deliberate
decision. HTTP_FINGERPRINT carries "yealink" as a heavy but *not* mandatory
rule, with the reason written next to it: some models answer / with a redirect
to their API page, whose body says "yealink" nowhere, and a mandatory rule
disqualifies the driver whenever its pattern is absent. Making it mandatory
would rule out a real phone.

The tests demanded it be mandatory, and the Cisco test then asserted `not
all(...)` over an empty list, which is False. Both now check the property that
actually protects against a false match: none of this driver's patterns appear
on a Cisco page, so it contributes no score and never wins.
This commit is contained in:
Christian Manivong
2026-08-21 13:07:37 +07:00
parent 459178a2f4
commit 33d07bed58
4 changed files with 27 additions and 11 deletions
Binary file not shown.
+2 -3
View File
@@ -21,14 +21,13 @@ import requests
from requests.exceptions import RequestException
from napalm.base.exceptions import ConnectionClosedException, ConnectionException
from napalm_device_types import AccessPointDriver, FingerprintRule
from napalm_device_types import FingerprintRule, PhoneDriver
from napalm_device_types.models import HealthMetricsDict
class YealinkDriver(AccessPointDriver):
class YealinkDriver(PhoneDriver):
"""NAPALM driver for Yealink IP phones (T-series, W-series, CP-series, VP-series)."""
TYPE_LABEL = "Phone"
VENDOR = "Yealink"
DRIVER_NAME = "yealink"
+25 -8
View File
@@ -1,6 +1,6 @@
"""Fingerprint tests for YealinkDriver."""
from napalm_device_types import DeviceTypeDriver, FingerprintRule
from napalm_device_types import DeviceTypeDriver, PhoneDriver, role_keys_of
from napalm_yealink import YealinkDriver
@@ -24,9 +24,18 @@ def test_snmp_oid_prefix():
assert YealinkDriver.SNMP_OBJECT_ID_PREFIX == "1.3.6.1.4.1.37403"
def test_http_fingerprint_mandatory_yealink():
mandatory = [r for r in YealinkDriver.HTTP_FINGERPRINT if r.mandatory]
assert any(r.pattern == "yealink" for r in mandatory)
def test_yealink_pattern_is_decisive_but_not_mandatory():
"""A mandatory rule disqualifies the driver whenever the pattern is absent.
Some Yealink models answer / with a redirect to their API page, whose body
carries no "yealink" anywhere — making the rule mandatory would rule out a
real Yealink phone. It carries decisive weight instead, so a page that does
say "yealink" still wins by a wide margin.
"""
yealink_rules = [r for r in YealinkDriver.HTTP_FINGERPRINT if r.pattern == "yealink"]
assert yealink_rules, "the vendor pattern must be present at all"
assert all(not r.mandatory for r in yealink_rules)
assert max(r.weight for r in yealink_rules) >= 10.0
def test_fingerprint_matches_t58_title():
@@ -36,8 +45,16 @@ def test_fingerprint_matches_t58_title():
assert all(r.pattern in combined for r in mandatory)
def test_fingerprint_does_not_match_cisco():
"""Cisco-Seite enthält kein 'yealink' → mandatory Pattern fehlt."""
def test_fingerprint_scores_nothing_on_a_cisco_page():
"""Without a mandatory rule, what keeps a Cisco phone out is that none of
Yealink's patterns match it at all — so the driver contributes zero score
and never wins."""
combined = "cisco ip phone cp-8841 "
mandatory = [r for r in YealinkDriver.HTTP_FINGERPRINT if r.mandatory]
assert not all(r.pattern in combined for r in mandatory)
assert not any(r.pattern in combined for r in YealinkDriver.HTTP_FINGERPRINT)
def test_declares_the_phone_role_not_access_point():
"""A desk phone used to inherit AccessPointDriver for want of anywhere
better, which put it in netOrk's wireless page and AP profile pickers."""
assert issubclass(YealinkDriver, PhoneDriver)
assert role_keys_of(YealinkDriver) == ["phone"]