mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-05-24 08:40:31 -04:00
Enhance documentation and implement SET_ALWAYS functionality for device name resolution #1650
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
# Quick Reference Guide - Device Field Lock/Unlock System
|
||||
|
||||
> For how scan overwrite rules (`SET_ALWAYS`, `SET_EMPTY`) and source tracking work under the hood, see [Device Source Fields](./DEVICE_SOURCE_FIELDS.md).
|
||||
|
||||
## Overview
|
||||
|
||||

|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Understanding Device Source Fields and Field Updates
|
||||
|
||||
> For the UI guide on locking and unlocking individual fields, see [Device Field Lock/Unlock](./DEVICE_FIELD_LOCK.md).
|
||||
|
||||
When the system scans a network, it finds various details about devices (like names, IP addresses, and manufacturers). To ensure the data remains accurate without accidentally overwriting manual changes, the system uses a set of "Source Rules."
|
||||
|
||||

|
||||
@@ -17,6 +19,8 @@ Every piece of information for a device has a **Source**. This source determines
|
||||
| **NEWDEV** | This value was initialized from `NEWDEV` plugin settings. | **Always** |
|
||||
| **(Plugin Name)** | The value was found by a specific scanner (e.g., `NBTSCAN`). | **Only if specific rules are met** |
|
||||
|
||||
> For how `USER` and `LOCKED` sources are set through the UI (lock/unlock buttons), see [Device Field Lock/Unlock](./DEVICE_FIELD_LOCK.md).
|
||||
|
||||
---
|
||||
|
||||
## How Scans Update Information
|
||||
@@ -34,6 +38,8 @@ Some plugins are configured to be "authoritative." If a field is in the **SET_AL
|
||||
* The scanner will **always** overwrite the current value with the new one.
|
||||
* *Note: It will still never overwrite a `USER` or `LOCKED` field.*
|
||||
|
||||
> On large networks, enabling `SET_ALWAYS` on name-resolution fields (e.g., `devName`) widens the resolution scope and increases DNS query volume. See [Performance — Plugin Field Authority](./PERFORMANCE.md#plugin-field-authority-set_always-and-set_empty) for details.
|
||||
|
||||
### 3. SET_EMPTY
|
||||
|
||||
If a field is in the **SET_EMPTY** list:
|
||||
|
||||
@@ -95,6 +95,41 @@ For example, the **ICMP plugin** allows scanning only IPs that match a specific
|
||||
|
||||
---
|
||||
|
||||
## Plugin Field Authority: `SET_ALWAYS` and `SET_EMPTY`
|
||||
|
||||
Plugins can be configured to control how aggressively they overwrite existing device field values via two settings:
|
||||
|
||||
| Setting | Behaviour |
|
||||
|---|---|
|
||||
| `<PLUGIN>_SET_ALWAYS` | Plugin always overwrites the field, as long as it can resolve a value and the field is not `USER`/`LOCKED` |
|
||||
| `<PLUGIN>_SET_EMPTY` | Plugin only writes when the field is currently empty |
|
||||
|
||||
Both settings accept a list of field names (e.g., `devName`, `devFQDN`). See [Name resolution](./NAME_RESOLUTION.md) and [Field locking](./DEVICE_SOURCE_FIELDS.md) docs for details.
|
||||
|
||||
### Performance Impact of `SET_ALWAYS` on Name Resolution
|
||||
|
||||
By default, name resolution (DIGSCAN, NBTSCAN, NSLOOKUP, AVAHISCAN) only runs against **devices that have no name yet**. This keeps DNS query volume proportional to new devices discovered, not the total inventory.
|
||||
|
||||
When **any** name-resolution plugin has `devName` in its `SET_ALWAYS` list, the system additionally runs a second resolution pass against **all devices whose name is not `USER`/`LOCKED` protected**. This allows a higher-priority plugin (e.g., DIGSCAN) to overwrite names previously set by a lower-priority one (e.g., NBTSCAN).
|
||||
|
||||
**Cost:** one DNS query per unprotected, already-named device per name-resolution cycle.
|
||||
|
||||
| Scenario | Devices resolved per cycle |
|
||||
|---|---|
|
||||
| No `SET_ALWAYS` on `devName` | Only new/unknown devices |
|
||||
| `SET_ALWAYS: devName` on any plugin | New/unknown devices **+** all unprotected named devices |
|
||||
|
||||
> [!WARNING]
|
||||
> On large installations (thousands of devices), enabling `SET_ALWAYS: devName` significantly increases DNS query volume and cycle duration. To mitigate:
|
||||
>
|
||||
> * Increase the scan interval of name-resolution plugins (`DIGSCAN_RUN_SCHD`, `NBTSCAN_RUN_SCHD`, etc.).
|
||||
> * Mark devices whose name should never change as `USER` or `LOCKED` — they are excluded from the re-resolve pass entirely.
|
||||
> * Use `SET_ALWAYS` only on the highest-priority plugin; leave lower-priority plugins without it.
|
||||
|
||||
The actual number of DB rows updated is logged at `verbose` level under `[Update Device Name] SET_ALWAYS re-resolve - DB rows updated`.
|
||||
|
||||
---
|
||||
|
||||
## Storing Temporary Files in Memory
|
||||
|
||||
On devices with slower I/O, you can improve performance by storing temporary files (and optionally the database) in memory using `tmpfs`.
|
||||
|
||||
@@ -79,6 +79,7 @@ nav:
|
||||
- Device Display Settings: DEVICE_DISPLAY_SETTINGS.md
|
||||
- Session Info: SESSION_INFO.md
|
||||
- Field Lock/Unlock: DEVICE_FIELD_LOCK.md
|
||||
- Device Source Fields: DEVICE_SOURCE_FIELDS.md
|
||||
- Icons and Topology:
|
||||
- Icons: ICONS.md
|
||||
- Network Topology: NETWORK_TREE.md
|
||||
|
||||
@@ -53,6 +53,15 @@ class DeviceInstance:
|
||||
WHERE devName IN ("(unknown)", "(name not found)", "")
|
||||
""")
|
||||
|
||||
def getResolvable(self):
|
||||
"""Return devices that have a name already set but are not USER/LOCKED protected.
|
||||
Used by SET_ALWAYS name-resolution plugins to re-resolve existing names."""
|
||||
return self._fetchall("""
|
||||
SELECT * FROM Devices
|
||||
WHERE devName NOT IN ("(unknown)", "(name not found)", "")
|
||||
AND COALESCE(devNameSource, '') NOT IN ('USER', 'LOCKED')
|
||||
""")
|
||||
|
||||
def getValueWithMac(self, column_name, devMac):
|
||||
row = self._fetchone(f"""
|
||||
SELECT {column_name} FROM Devices WHERE devMac = ?
|
||||
|
||||
@@ -1090,6 +1090,71 @@ def update_devices_names(pm):
|
||||
plugin_records,
|
||||
)
|
||||
|
||||
# --- Step 1b: Re-resolve already-named devices for SET_ALWAYS plugins ---
|
||||
# If any name-resolution plugin declares devName in SET_ALWAYS, it should be
|
||||
# able to overwrite names set by lower-priority plugins. Step 1 only covers
|
||||
# unknown devices, so we run a second pass here limited to devices that:
|
||||
# - already have a name (not unknown/empty), AND
|
||||
# - are not USER/LOCKED protected
|
||||
# recordsNotFound is intentionally discarded: if resolution fails, the
|
||||
# existing name is kept as-is.
|
||||
name_resolution_plugins = [label for _, label in strategies]
|
||||
set_always_plugins = [
|
||||
p for p in name_resolution_plugins
|
||||
if "devName" in get_plugin_authoritative_settings(p).get("set_always", [])
|
||||
]
|
||||
|
||||
if not set_always_plugins:
|
||||
mylog("debug", "[Update Device Name] SET_ALWAYS re-resolve: skipped (no name-resolution plugin has devName in SET_ALWAYS)")
|
||||
else:
|
||||
resolvableDevices = device_handler.getResolvable()
|
||||
mylog("debug", f"[Update Device Name] SET_ALWAYS re-resolve: active plugins={set_always_plugins}, candidate devices={len(resolvableDevices)}")
|
||||
|
||||
if resolvableDevices:
|
||||
recordsToUpdate, _, fs, notFound = resolve_devices(resolvableDevices)
|
||||
|
||||
res_string = f"{fs['DIGSCAN']}/{fs['AVAHISCAN']}/{fs['NSLOOKUP']}/{fs['NBTSCAN']}"
|
||||
mylog("verbose", f"[Update Device Name] SET_ALWAYS re-resolve - Found (DIG/AVAHI/NSL/NBT): {len(recordsToUpdate)} ({res_string}), Not Found: {notFound}")
|
||||
|
||||
records_by_plugin = {}
|
||||
for entry in recordsToUpdate:
|
||||
records_by_plugin.setdefault(entry[1], []).append(entry)
|
||||
|
||||
total_updated = 0
|
||||
for plugin_label, plugin_records in records_by_plugin.items():
|
||||
plugin_settings = get_plugin_authoritative_settings(plugin_label)
|
||||
name_clause = get_overwrite_sql_clause(
|
||||
"devName", "devNameSource", plugin_settings
|
||||
)
|
||||
fqdn_clause = get_overwrite_sql_clause(
|
||||
"devFQDN", "devFQDNSource", plugin_settings
|
||||
)
|
||||
|
||||
sql.executemany(
|
||||
f"""UPDATE Devices
|
||||
SET devName = CASE
|
||||
WHEN {name_clause} THEN ?
|
||||
ELSE devName
|
||||
END,
|
||||
devNameSource = CASE
|
||||
WHEN {name_clause} THEN ?
|
||||
ELSE devNameSource
|
||||
END,
|
||||
devFQDN = CASE
|
||||
WHEN {fqdn_clause} THEN ?
|
||||
ELSE devFQDN
|
||||
END,
|
||||
devFQDNSource = CASE
|
||||
WHEN {fqdn_clause} THEN ?
|
||||
ELSE devFQDNSource
|
||||
END
|
||||
WHERE devMac = ?""",
|
||||
plugin_records,
|
||||
)
|
||||
total_updated += sql.rowcount
|
||||
|
||||
mylog("verbose", f"[Update Device Name] SET_ALWAYS re-resolve - DB rows updated: {total_updated}")
|
||||
|
||||
# --- Step 2: Optionally refresh FQDN for all devices ---
|
||||
if get_setting_value("REFRESH_FQDN"):
|
||||
allDevices = device_handler.getAll()
|
||||
|
||||
Reference in New Issue
Block a user