Add machinetracker collapse-duplicates option#4099
Conversation
lunkwill42
left a comment
There was a problem hiding this comment.
Thanks for this — the design is sound and correctly opt-in, the disjoint-history preservation is right, and the collapsed row count is plumbed through to the counters correctly. I ran the machinetracker integration suite locally and it's green.
Having this as a single commit is borderline, but I'll allow it.
A few inline notes:
- The
collapse_overlapping/create_active_rowsuggestions are optional polish (a type annotation on the new helper, and thesetdefaultidiom) — the original codebase carries no type annotations, as those did not exist in Python at the time, but it's always good to see it on new code. - One I'd like before merge: the tests assert how many rows survive but never which one, so the headline "keep the newest" guarantee is currently untested (see the test-file comment).
Only other thing: the contributor checklist still has the before/after screenshots unchecked — since this adds a checkbox to the IP search form, could you attach those to the PR description?
| return result | ||
|
|
||
|
|
||
| def collapse_overlapping(rows): |
There was a problem hiding this comment.
Optional: annotate the new helper. The rows are Arp instances (they come straight from get_arp_records), and your docstring already calls them "Arp result rows", so the type just formalises that:
| def collapse_overlapping(rows): | |
| def collapse_overlapping(rows: list[Arp]) -> list[Arp]: |
If you take this, add Arp to the import on line 28 so the name resolves:
from nav.models.manage import Arp, Prefix, Netbox, Interface
| """Creates a tracker tuple where the result is active""" | ||
| ip = str(ip_key) | ||
| rows = ip_result[ip_key] | ||
| touched_keys = [] |
There was a problem hiding this comment.
Optional: a set lets the dedup happen for free (pairs with the setdefault change below).
| touched_keys = [] | |
| touched_keys = set() |
| key = (row.ip, row.mac) | ||
| if key not in tracker: | ||
| tracker[key] = [] | ||
| touched_keys.append(key) | ||
| tracker[key].append(row) |
There was a problem hiding this comment.
Optional: setdefault is the idiomatic append-to-dict-of-lists, and with touched_keys as a set you no longer need the "first time only" guard:
| key = (row.ip, row.mac) | |
| if key not in tracker: | |
| tracker[key] = [] | |
| touched_keys.append(key) | |
| tracker[key].append(row) | |
| key = (row.ip, row.mac) | |
| tracker.setdefault(key, []).append(row) | |
| touched_keys.add(key) |
This is behaviour-preserving.
| _row(now - timedelta(hours=1), INFINITY, "router-a"), | ||
| _row(now - timedelta(hours=2), INFINITY, "router-b"), | ||
| ] | ||
| assert len(collapse_overlapping(rows)) == 1 |
There was a problem hiding this comment.
The whole point of the feature is that the newest row is the survivor, but nothing here asserts which row is kept — a collapse that kept the oldest would pass this test unchanged. Worth pinning down (router-a has the newer start_time):
| assert len(collapse_overlapping(rows)) == 1 | |
| kept = collapse_overlapping(rows) | |
| assert len(kept) == 1 | |
| assert kept[0].sysname == "router-a" # newer start_time wins |
98bbfa5 to
ccf4c87
Compare
NAV records one Arp row per collecting router by design, so an address served by several routers (e.g. an HSRP pair) appears as multiple overlapping rows in a machinetracker IP search for active addresses. Add an opt-in "Collapse duplicates" checkbox to the IP search form. When enabled, rows for the same (ip, mac) whose active time windows overlap are collapsed to one (newest kept). Disjoint windows (genuine connect/disconnect history) are preserved. Default off, so existing behavior is unchanged. Note: the newest row is kept whole -- its own time window is shown, not a union of the duplicates' windows. If a dropped duplicate had a later end_time, that later end is not reflected. Fixes Uninett#4098 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Address review feedback on the collapse-duplicates option: - Pin the "keep newest" guarantee: the collapse test asserted only how many rows survived, not which one, so a collapse that kept the oldest would have passed. Assert the newer-start_time row wins. - Annotate collapse_overlapping() with list[Arp] -> list[Arp] and import Arp, formalising what the docstring already states. - Use the setdefault dict-of-lists idiom in create_active_row and track touched keys in a set, dropping the first-time-only guard. Behaviour-preserving. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ccf4c87 to
d5bac8d
Compare
|
|
Thanks for the review! Applied all of it:
While in there I also flagged a design caveat in the commit message: the newest row is kept whole — its own window is shown, not a union of the duplicates' windows, so a dropped duplicate's later Rebased on latest |



Scope and purpose
Fixes #4098.
NAV records one
Arprow per collecting router by design, so an address served by several routers (e.g. an HSRP pair) shows up as multiple overlapping rows in a machinetracker IP search for active addresses.This adds an opt-in "Collapse duplicates" checkbox to the IP search form:
(ip, mac)whose active time windows overlap are merged into one (newest kept).Disjoint windows (genuine connect/disconnect history) are preserved — overlap test is
s1 < e2 and s2 < e1. Still-active rows (end_time = INFINITY) always overlap, so they collapse to one.This pull request
Contributor Checklist
masterHow to observe
arprows where the same(ip, mac)is collected by two routers — two rows, same ip/mac, bothend_time = infinity, differentsysname/netbox.Trade-off
With collapse on, the kept row is the newest, so the dropped router's name in the Source column is not shown. Acceptable since the option is opt-in. A future enhancement could annotate "seen by N routers".
Screenshots
🤖 Generated with Claude Code