feat: alternative fingerprint rules count once (4.8.0)
Discovery compares drivers by score divided by the most a driver could score, and that maximum summed every positive rule. Rules that never match together, such as one vendor's landing page or another's, made a driver that knows several vendors lose to one that knows none: napalm-redfish's Supermicro landing page scored 6 of 41 and lost to a bare "linux" in the same BMC's SNMP sysDescr, 5 of 18. FingerprintRule gets an optional group. rules_max_weight() sums the positive rules and counts each group once, with its strongest rule. Existing rules have no group and count as before.
This commit is contained in:
1 parent
7afa590c0c
commit
a1da0337e2
5 files changed
+97
-2
No files matched your search
@@ -60,7 +60,7 @@ Introspection -- :func:`~napalm_device_types.roles.roles_of`,
|
||||
:func:`~napalm_device_types.roles.primary_role_of`.
|
||||
"""
|
||||
|
||||
from napalm_device_types.base import DeviceTypeDriver, FingerprintRule, PortSpec
|
||||
from napalm_device_types.base import DeviceTypeDriver, FingerprintRule, PortSpec, rules_max_weight
|
||||
from napalm_device_types.access_point import AccessPointDriver
|
||||
from napalm_device_types.channel import (
|
||||
ByteStream,
|
||||
@@ -156,6 +156,7 @@ __all__ = [
|
||||
"DeviceTypeDriver",
|
||||
"DhcpServerMixin",
|
||||
"FingerprintRule",
|
||||
"rules_max_weight",
|
||||
"FirewallDriver",
|
||||
"FirewallRuleMixin",
|
||||
"HealthMetricsMixin",
|
||||
|
||||
@@ -8,6 +8,7 @@ live connection.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Iterable
|
||||
from typing import NamedTuple
|
||||
|
||||
from napalm.base import NetworkDriver
|
||||
@@ -24,12 +25,36 @@ class FingerprintRule(NamedTuple):
|
||||
weight — evidence added to the driver's score on match
|
||||
mandatory — pattern absent → driver immediately disqualified
|
||||
negative — pattern present → weight subtracted instead of added
|
||||
group — rules sharing a group are alternatives that never match
|
||||
together (one vendor's landing page *or* another's); only the
|
||||
strongest of them counts towards the most a driver can score
|
||||
"""
|
||||
|
||||
pattern: str
|
||||
weight: float = 1.0
|
||||
mandatory: bool = False
|
||||
negative: bool = False
|
||||
group: str | None = None
|
||||
|
||||
|
||||
def rules_max_weight(rules: Iterable[FingerprintRule]) -> float:
|
||||
"""The most evidence *rules* can add together.
|
||||
|
||||
Every positive rule counts, except that a group of alternatives counts once,
|
||||
with its strongest rule. Discovery divides a driver's score by its maximum to
|
||||
compare drivers; summing alternatives would make a driver that recognises
|
||||
three vendors' pages lose to one that recognises none.
|
||||
"""
|
||||
total = 0.0
|
||||
group_best: dict[str, float] = {}
|
||||
for rule in rules:
|
||||
if rule.negative or rule.weight <= 0:
|
||||
continue
|
||||
if rule.group is None:
|
||||
total += rule.weight
|
||||
else:
|
||||
group_best[rule.group] = max(group_best.get(rule.group, 0.0), rule.weight)
|
||||
return total + sum(group_best.values())
|
||||
|
||||
|
||||
class PortSpec(NamedTuple):
|
||||
|
||||
Reference in new issue
Block a user