mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-03-30 23:03:03 -07:00
Refactor authoritative field handling and enhance device update logic
- Updated `get_source_for_field_update_with_value` to determine source values based on new field values, including handling for empty and unknown values. - Introduced `get_overwrite_sql_clause` to build SQL conditions for authoritative overwrite checks based on plugin settings. - Enhanced `update_devices_data_from_scan` to utilize new authoritative settings and conditions for updating device fields. - Added new tests for source value determination and device creation to ensure proper handling of source fields. - Created in-memory SQLite database fixtures for testing device creation and updates.
This commit is contained in:
@@ -601,7 +601,7 @@ function toggleFieldLock(mac, fieldName) {
|
|||||||
const sourceIndicator = lockBtn.next();
|
const sourceIndicator = lockBtn.next();
|
||||||
if (sourceIndicator.hasClass("input-group-addon")) {
|
if (sourceIndicator.hasClass("input-group-addon")) {
|
||||||
const sourceValue = shouldLock ? "LOCKED" : "UNKNOWN";
|
const sourceValue = shouldLock ? "LOCKED" : "UNKNOWN";
|
||||||
const sourceClass = shouldLock ? "input-group-addon text-danger" : "input-group-addon text-muted";
|
const sourceClass = shouldLock ? "input-group-addon text-danger" : "input-group-addon pointer text-muted";
|
||||||
sourceIndicator.text(sourceValue);
|
sourceIndicator.text(sourceValue);
|
||||||
sourceIndicator.attr("class", sourceClass);
|
sourceIndicator.attr("class", sourceClass);
|
||||||
sourceIndicator.attr("title", getString("FieldLock_Source_Label") + sourceValue);
|
sourceIndicator.attr("title", getString("FieldLock_Source_Label") + sourceValue);
|
||||||
|
|||||||
@@ -118,21 +118,64 @@ def can_overwrite_field(field_name, current_source, plugin_prefix, plugin_settin
|
|||||||
return not current_source or current_source == "NEWDEV"
|
return not current_source or current_source == "NEWDEV"
|
||||||
|
|
||||||
|
|
||||||
def get_source_for_field_update(field_name, plugin_prefix, is_user_override=False):
|
def get_overwrite_sql_clause(field_name, source_column, plugin_settings):
|
||||||
"""
|
"""
|
||||||
Determine what source value should be set when a field is updated.
|
Build a SQL condition for authoritative overwrite checks.
|
||||||
|
|
||||||
|
Returns a SQL snippet that permits overwrite for the given field
|
||||||
|
based on SET_ALWAYS/SET_EMPTY and USER/LOCKED protection.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
field_name: The field being updated (e.g., "devName").
|
||||||
|
source_column: The *Source column name (e.g., "devNameSource").
|
||||||
|
plugin_settings: dict with "set_always" and "set_empty" lists.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: SQL condition snippet (no leading WHERE).
|
||||||
|
"""
|
||||||
|
set_always = plugin_settings.get("set_always", [])
|
||||||
|
set_empty = plugin_settings.get("set_empty", [])
|
||||||
|
|
||||||
|
if field_name in set_always:
|
||||||
|
return f"COALESCE({source_column}, '') NOT IN ('USER', 'LOCKED')"
|
||||||
|
|
||||||
|
if field_name in set_empty or field_name not in set_always:
|
||||||
|
return f"COALESCE({source_column}, '') IN ('', 'NEWDEV')"
|
||||||
|
|
||||||
|
return f"COALESCE({source_column}, '') IN ('', 'NEWDEV')"
|
||||||
|
|
||||||
|
|
||||||
|
def get_source_for_field_update_with_value(
|
||||||
|
field_name, plugin_prefix, field_value, is_user_override=False
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Determine the source value for a field update based on the new value.
|
||||||
|
|
||||||
|
If the new value is empty or an "unknown" placeholder, return NEWDEV.
|
||||||
|
Otherwise, fall back to standard source selection rules.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
field_name: The field being updated.
|
field_name: The field being updated.
|
||||||
plugin_prefix: The unique prefix of the plugin writing (e.g., "UNIFIAPI").
|
plugin_prefix: The unique prefix of the plugin writing (e.g., "UNIFIAPI").
|
||||||
Ignored if is_user_override is True.
|
field_value: The new value being written.
|
||||||
is_user_override: If True, return "USER"; if False, return plugin_prefix.
|
is_user_override: If True, return "USER".
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The source value to set for the *Source field.
|
str: The source value to set for the *Source field.
|
||||||
"""
|
"""
|
||||||
if is_user_override:
|
if is_user_override:
|
||||||
return "USER"
|
return "USER"
|
||||||
|
|
||||||
|
if field_value is None:
|
||||||
|
return "NEWDEV"
|
||||||
|
|
||||||
|
if isinstance(field_value, str):
|
||||||
|
stripped = field_value.strip()
|
||||||
|
if stripped in ("", "null"):
|
||||||
|
return "NEWDEV"
|
||||||
|
if stripped.lower() in ("(unknown)", "(name not found)"):
|
||||||
|
return "NEWDEV"
|
||||||
|
|
||||||
return plugin_prefix
|
return plugin_prefix
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ from models.device_instance import DeviceInstance
|
|||||||
from scan.name_resolution import NameResolver
|
from scan.name_resolution import NameResolver
|
||||||
from scan.device_heuristics import guess_icon, guess_type
|
from scan.device_heuristics import guess_icon, guess_type
|
||||||
from db.db_helper import sanitize_SQL_input, list_to_where, safe_int
|
from db.db_helper import sanitize_SQL_input, list_to_where, safe_int
|
||||||
|
from db.authoritative_handler import (
|
||||||
|
get_overwrite_sql_clause,
|
||||||
|
get_plugin_authoritative_settings,
|
||||||
|
get_source_for_field_update_with_value,
|
||||||
|
)
|
||||||
from helper import format_ip_long
|
from helper import format_ip_long
|
||||||
|
|
||||||
# Make sure log level is initialized correctly
|
# Make sure log level is initialized correctly
|
||||||
@@ -56,6 +61,15 @@ def update_devices_data_from_scan(db):
|
|||||||
sql = db.sql # TO-DO
|
sql = db.sql # TO-DO
|
||||||
startTime = timeNowDB()
|
startTime = timeNowDB()
|
||||||
|
|
||||||
|
device_columns = set()
|
||||||
|
try:
|
||||||
|
device_columns = {row["name"] for row in sql.execute("PRAGMA table_info(Devices)").fetchall()}
|
||||||
|
except Exception:
|
||||||
|
device_columns = set()
|
||||||
|
|
||||||
|
def has_column(column_name):
|
||||||
|
return column_name in device_columns if device_columns else False
|
||||||
|
|
||||||
# Update Last Connection
|
# Update Last Connection
|
||||||
mylog("debug", "[Update Devices] 1 Last Connection")
|
mylog("debug", "[Update Devices] 1 Last Connection")
|
||||||
sql.execute(f"""UPDATE Devices SET devLastConnection = '{startTime}',
|
sql.execute(f"""UPDATE Devices SET devLastConnection = '{startTime}',
|
||||||
@@ -69,87 +83,464 @@ def update_devices_data_from_scan(db):
|
|||||||
WHERE NOT EXISTS (SELECT 1 FROM CurrentScan
|
WHERE NOT EXISTS (SELECT 1 FROM CurrentScan
|
||||||
WHERE devMac = cur_MAC) """)
|
WHERE devMac = cur_MAC) """)
|
||||||
|
|
||||||
# Update IP (devLastIP always updated, primary IPv4/IPv6 set based on family)
|
plugin_rows = sql.execute(
|
||||||
mylog("debug", "[Update Devices] - cur_IP -> devLastIP / devPrimaryIPv4 / devPrimaryIPv6")
|
"SELECT DISTINCT cur_ScanMethod FROM CurrentScan"
|
||||||
sql.execute("""
|
).fetchall()
|
||||||
WITH LatestIP AS (
|
plugin_prefixes = [row[0] for row in plugin_rows if row[0]]
|
||||||
SELECT c.cur_MAC AS mac, c.cur_IP AS ip
|
if not plugin_prefixes:
|
||||||
FROM CurrentScan c
|
plugin_prefixes = [None]
|
||||||
WHERE c.cur_IP IS NOT NULL
|
plugin_settings_cache = {}
|
||||||
AND c.cur_IP NOT IN ('', 'null', '(unknown)', '(Unknown)')
|
|
||||||
AND c.cur_DateTime = (
|
|
||||||
SELECT MAX(c2.cur_DateTime)
|
|
||||||
FROM CurrentScan c2
|
|
||||||
WHERE c2.cur_MAC = c.cur_MAC
|
|
||||||
AND c2.cur_IP IS NOT NULL
|
|
||||||
AND c2.cur_IP NOT IN ('', 'null', '(unknown)', '(Unknown)')
|
|
||||||
)
|
|
||||||
)
|
|
||||||
UPDATE Devices
|
|
||||||
SET devLastIP = (SELECT ip FROM LatestIP WHERE mac = devMac),
|
|
||||||
devPrimaryIPv4 = CASE
|
|
||||||
WHEN (SELECT ip FROM LatestIP WHERE mac = devMac) LIKE '%:%' THEN devPrimaryIPv4
|
|
||||||
ELSE (SELECT ip FROM LatestIP WHERE mac = devMac)
|
|
||||||
END,
|
|
||||||
devPrimaryIPv6 = CASE
|
|
||||||
WHEN (SELECT ip FROM LatestIP WHERE mac = devMac) LIKE '%:%' THEN (SELECT ip FROM LatestIP WHERE mac = devMac)
|
|
||||||
ELSE devPrimaryIPv6
|
|
||||||
END
|
|
||||||
WHERE EXISTS (SELECT 1 FROM LatestIP WHERE mac = devMac);
|
|
||||||
""")
|
|
||||||
|
|
||||||
# Update only devices with empty, NULL or (u(U)nknown) vendors
|
def get_plugin_settings_cached(plugin_prefix):
|
||||||
mylog("debug", "[Update Devices] - cur_Vendor -> (if empty) devVendor")
|
if plugin_prefix not in plugin_settings_cache:
|
||||||
sql.execute("""UPDATE Devices
|
plugin_settings_cache[plugin_prefix] = get_plugin_authoritative_settings(
|
||||||
|
plugin_prefix
|
||||||
|
)
|
||||||
|
return plugin_settings_cache[plugin_prefix]
|
||||||
|
|
||||||
|
for plugin_prefix in plugin_prefixes:
|
||||||
|
filter_by_scan_method = plugin_prefix is not None and plugin_prefix != ""
|
||||||
|
source_prefix = plugin_prefix if filter_by_scan_method else "NEWDEV"
|
||||||
|
plugin_settings = get_plugin_settings_cached(source_prefix)
|
||||||
|
|
||||||
|
has_last_ip_source = has_column("devLastIpSource")
|
||||||
|
has_vendor_source = has_column("devVendorSource")
|
||||||
|
has_parent_port_source = has_column("devParentPortSource")
|
||||||
|
has_parent_mac_source = has_column("devParentMacSource")
|
||||||
|
has_ssid_source = has_column("devSsidSource")
|
||||||
|
has_name_source = has_column("devNameSource")
|
||||||
|
|
||||||
|
dev_last_ip_clause = (
|
||||||
|
get_overwrite_sql_clause("devLastIP", "devLastIpSource", plugin_settings)
|
||||||
|
if has_last_ip_source
|
||||||
|
else "1=1"
|
||||||
|
)
|
||||||
|
dev_vendor_clause = (
|
||||||
|
get_overwrite_sql_clause("devVendor", "devVendorSource", plugin_settings)
|
||||||
|
if has_vendor_source
|
||||||
|
else "1=1"
|
||||||
|
)
|
||||||
|
dev_parent_port_clause = (
|
||||||
|
get_overwrite_sql_clause("devParentPort", "devParentPortSource", plugin_settings)
|
||||||
|
if has_parent_port_source
|
||||||
|
else "1=1"
|
||||||
|
)
|
||||||
|
dev_parent_mac_clause = (
|
||||||
|
get_overwrite_sql_clause("devParentMAC", "devParentMacSource", plugin_settings)
|
||||||
|
if has_parent_mac_source
|
||||||
|
else "1=1"
|
||||||
|
)
|
||||||
|
dev_ssid_clause = (
|
||||||
|
get_overwrite_sql_clause("devSSID", "devSsidSource", plugin_settings)
|
||||||
|
if has_ssid_source
|
||||||
|
else "1=1"
|
||||||
|
)
|
||||||
|
dev_name_clause = (
|
||||||
|
get_overwrite_sql_clause("devName", "devNameSource", plugin_settings)
|
||||||
|
if has_name_source
|
||||||
|
else "1=1"
|
||||||
|
)
|
||||||
|
|
||||||
|
name_is_set_always = "devName" in plugin_settings.get("set_always", [])
|
||||||
|
vendor_is_set_always = "devVendor" in plugin_settings.get("set_always", [])
|
||||||
|
parent_port_is_set_always = "devParentPort" in plugin_settings.get("set_always", [])
|
||||||
|
parent_mac_is_set_always = "devParentMAC" in plugin_settings.get("set_always", [])
|
||||||
|
ssid_is_set_always = "devSSID" in plugin_settings.get("set_always", [])
|
||||||
|
|
||||||
|
name_empty_condition = "1=1" if name_is_set_always else (
|
||||||
|
"(devName IN ('(unknown)', '(name not found)', '') OR devName IS NULL)"
|
||||||
|
)
|
||||||
|
vendor_empty_condition = "1=1" if vendor_is_set_always else (
|
||||||
|
"(devVendor IS NULL OR devVendor IN ('', 'null', '(unknown)', '(Unknown)'))"
|
||||||
|
)
|
||||||
|
parent_port_empty_condition = "1=1" if parent_port_is_set_always else (
|
||||||
|
"(devParentPort IS NULL OR devParentPort IN ('', 'null', '(unknown)', '(Unknown)'))"
|
||||||
|
)
|
||||||
|
parent_mac_empty_condition = "1=1" if parent_mac_is_set_always else (
|
||||||
|
"(devParentMAC IS NULL OR devParentMAC IN ('', 'null', '(unknown)', '(Unknown)'))"
|
||||||
|
)
|
||||||
|
ssid_empty_condition = "1=1" if ssid_is_set_always else (
|
||||||
|
"(devSSID IS NULL OR devSSID IN ('', 'null'))"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Update IP (devLastIP always updated, primary IPv4/IPv6 set based on family)
|
||||||
|
mylog(
|
||||||
|
"debug",
|
||||||
|
f"[Update Devices] - ({source_prefix}) cur_IP -> devLastIP / devPrimaryIPv4 / devPrimaryIPv6",
|
||||||
|
)
|
||||||
|
last_ip_source_fragment = ", devLastIpSource = ?" if has_last_ip_source else ""
|
||||||
|
last_ip_params = (source_prefix,) if has_last_ip_source else ()
|
||||||
|
|
||||||
|
if filter_by_scan_method:
|
||||||
|
sql.execute(
|
||||||
|
f"""
|
||||||
|
WITH LatestIP AS (
|
||||||
|
SELECT c.cur_MAC AS mac, c.cur_IP AS ip
|
||||||
|
FROM CurrentScan c
|
||||||
|
WHERE c.cur_IP IS NOT NULL
|
||||||
|
AND c.cur_IP NOT IN ('', 'null', '(unknown)', '(Unknown)')
|
||||||
|
AND c.cur_ScanMethod = ?
|
||||||
|
AND c.cur_DateTime = (
|
||||||
|
SELECT MAX(c2.cur_DateTime)
|
||||||
|
FROM CurrentScan c2
|
||||||
|
WHERE c2.cur_MAC = c.cur_MAC
|
||||||
|
AND c2.cur_IP IS NOT NULL
|
||||||
|
AND c2.cur_IP NOT IN ('', 'null', '(unknown)', '(Unknown)')
|
||||||
|
AND c2.cur_ScanMethod = ?
|
||||||
|
)
|
||||||
|
)
|
||||||
|
UPDATE Devices
|
||||||
|
SET devLastIP = (SELECT ip FROM LatestIP WHERE mac = devMac),
|
||||||
|
devPrimaryIPv4 = CASE
|
||||||
|
WHEN (SELECT ip FROM LatestIP WHERE mac = devMac) LIKE '%:%' THEN devPrimaryIPv4
|
||||||
|
ELSE (SELECT ip FROM LatestIP WHERE mac = devMac)
|
||||||
|
END,
|
||||||
|
devPrimaryIPv6 = CASE
|
||||||
|
WHEN (SELECT ip FROM LatestIP WHERE mac = devMac) LIKE '%:%' THEN (SELECT ip FROM LatestIP WHERE mac = devMac)
|
||||||
|
ELSE devPrimaryIPv6
|
||||||
|
END
|
||||||
|
{last_ip_source_fragment}
|
||||||
|
WHERE EXISTS (SELECT 1 FROM LatestIP WHERE mac = devMac)
|
||||||
|
AND {dev_last_ip_clause};
|
||||||
|
""",
|
||||||
|
(plugin_prefix, plugin_prefix, *last_ip_params),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
sql.execute(
|
||||||
|
f"""
|
||||||
|
WITH LatestIP AS (
|
||||||
|
SELECT c.cur_MAC AS mac, c.cur_IP AS ip
|
||||||
|
FROM CurrentScan c
|
||||||
|
WHERE c.cur_IP IS NOT NULL
|
||||||
|
AND c.cur_IP NOT IN ('', 'null', '(unknown)', '(Unknown)')
|
||||||
|
AND c.cur_DateTime = (
|
||||||
|
SELECT MAX(c2.cur_DateTime)
|
||||||
|
FROM CurrentScan c2
|
||||||
|
WHERE c2.cur_MAC = c.cur_MAC
|
||||||
|
AND c2.cur_IP IS NOT NULL
|
||||||
|
AND c2.cur_IP NOT IN ('', 'null', '(unknown)', '(Unknown)')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
UPDATE Devices
|
||||||
|
SET devLastIP = (SELECT ip FROM LatestIP WHERE mac = devMac),
|
||||||
|
devPrimaryIPv4 = CASE
|
||||||
|
WHEN (SELECT ip FROM LatestIP WHERE mac = devMac) LIKE '%:%' THEN devPrimaryIPv4
|
||||||
|
ELSE (SELECT ip FROM LatestIP WHERE mac = devMac)
|
||||||
|
END,
|
||||||
|
devPrimaryIPv6 = CASE
|
||||||
|
WHEN (SELECT ip FROM LatestIP WHERE mac = devMac) LIKE '%:%' THEN (SELECT ip FROM LatestIP WHERE mac = devMac)
|
||||||
|
ELSE devPrimaryIPv6
|
||||||
|
END
|
||||||
|
{last_ip_source_fragment}
|
||||||
|
WHERE EXISTS (SELECT 1 FROM LatestIP WHERE mac = devMac)
|
||||||
|
AND {dev_last_ip_clause};
|
||||||
|
""",
|
||||||
|
last_ip_params,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Update vendor
|
||||||
|
mylog("debug", f"[Update Devices] - ({source_prefix}) cur_Vendor -> devVendor")
|
||||||
|
vendor_source_fragment = ", devVendorSource = ?" if has_vendor_source else ""
|
||||||
|
vendor_params = (source_prefix,) if has_vendor_source else ()
|
||||||
|
|
||||||
|
if filter_by_scan_method:
|
||||||
|
sql.execute(
|
||||||
|
f"""
|
||||||
|
UPDATE Devices
|
||||||
SET devVendor = (
|
SET devVendor = (
|
||||||
SELECT cur_Vendor
|
SELECT cur_Vendor
|
||||||
FROM CurrentScan
|
FROM CurrentScan
|
||||||
WHERE Devices.devMac = CurrentScan.cur_MAC
|
WHERE Devices.devMac = CurrentScan.cur_MAC
|
||||||
|
AND CurrentScan.cur_ScanMethod = ?
|
||||||
|
AND CurrentScan.cur_Vendor IS NOT NULL
|
||||||
|
AND CurrentScan.cur_Vendor NOT IN ('', 'null', '(unknown)', '(Unknown)')
|
||||||
|
ORDER BY CurrentScan.cur_DateTime DESC
|
||||||
|
LIMIT 1
|
||||||
)
|
)
|
||||||
WHERE
|
{vendor_source_fragment}
|
||||||
(devVendor IS NULL OR devVendor IN ("", "null", "(unknown)", "(Unknown)"))
|
WHERE {vendor_empty_condition}
|
||||||
AND EXISTS (
|
AND EXISTS (
|
||||||
SELECT 1
|
SELECT 1
|
||||||
FROM CurrentScan
|
FROM CurrentScan
|
||||||
WHERE Devices.devMac = CurrentScan.cur_MAC
|
WHERE Devices.devMac = CurrentScan.cur_MAC
|
||||||
)""")
|
AND CurrentScan.cur_ScanMethod = ?
|
||||||
|
AND CurrentScan.cur_Vendor IS NOT NULL
|
||||||
|
AND CurrentScan.cur_Vendor NOT IN ('', 'null', '(unknown)', '(Unknown)')
|
||||||
|
)
|
||||||
|
AND {dev_vendor_clause}
|
||||||
|
""",
|
||||||
|
(plugin_prefix, plugin_prefix, *vendor_params),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
sql.execute(
|
||||||
|
f"""
|
||||||
|
UPDATE Devices
|
||||||
|
SET devVendor = (
|
||||||
|
SELECT cur_Vendor
|
||||||
|
FROM CurrentScan
|
||||||
|
WHERE Devices.devMac = CurrentScan.cur_MAC
|
||||||
|
AND CurrentScan.cur_Vendor IS NOT NULL
|
||||||
|
AND CurrentScan.cur_Vendor NOT IN ('', 'null', '(unknown)', '(Unknown)')
|
||||||
|
ORDER BY CurrentScan.cur_DateTime DESC
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
{vendor_source_fragment}
|
||||||
|
WHERE {vendor_empty_condition}
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM CurrentScan
|
||||||
|
WHERE Devices.devMac = CurrentScan.cur_MAC
|
||||||
|
AND CurrentScan.cur_Vendor IS NOT NULL
|
||||||
|
AND CurrentScan.cur_Vendor NOT IN ('', 'null', '(unknown)', '(Unknown)')
|
||||||
|
)
|
||||||
|
AND {dev_vendor_clause}
|
||||||
|
""",
|
||||||
|
vendor_params,
|
||||||
|
)
|
||||||
|
|
||||||
# Update only devices with empty or NULL devParentPort
|
# Update parent port
|
||||||
mylog("debug", "[Update Devices] - (if not empty) cur_Port -> devParentPort")
|
mylog("debug", f"[Update Devices] - ({source_prefix}) cur_Port -> devParentPort")
|
||||||
sql.execute("""UPDATE Devices
|
parent_port_source_fragment = ", devParentPortSource = ?" if has_parent_port_source else ""
|
||||||
|
parent_port_params = (source_prefix,) if has_parent_port_source else ()
|
||||||
|
|
||||||
|
if filter_by_scan_method:
|
||||||
|
sql.execute(
|
||||||
|
f"""
|
||||||
|
UPDATE Devices
|
||||||
SET devParentPort = (
|
SET devParentPort = (
|
||||||
SELECT cur_Port
|
SELECT cur_Port
|
||||||
FROM CurrentScan
|
FROM CurrentScan
|
||||||
WHERE Devices.devMac = CurrentScan.cur_MAC
|
WHERE Devices.devMac = CurrentScan.cur_MAC
|
||||||
)
|
AND CurrentScan.cur_ScanMethod = ?
|
||||||
WHERE
|
AND CurrentScan.cur_Port IS NOT NULL
|
||||||
(devParentPort IS NULL OR devParentPort IN ("", "null", "(unknown)", "(Unknown)"))
|
AND CurrentScan.cur_Port NOT IN ('', 'null')
|
||||||
AND
|
ORDER BY CurrentScan.cur_DateTime DESC
|
||||||
EXISTS (
|
LIMIT 1
|
||||||
SELECT 1
|
)
|
||||||
FROM CurrentScan
|
{parent_port_source_fragment}
|
||||||
WHERE Devices.devMac = CurrentScan.cur_MAC
|
WHERE {parent_port_empty_condition}
|
||||||
AND CurrentScan.cur_Port IS NOT NULL AND CurrentScan.cur_Port NOT IN ("", "null")
|
AND EXISTS (
|
||||||
)""")
|
SELECT 1
|
||||||
|
FROM CurrentScan
|
||||||
|
WHERE Devices.devMac = CurrentScan.cur_MAC
|
||||||
|
AND CurrentScan.cur_ScanMethod = ?
|
||||||
|
AND CurrentScan.cur_Port IS NOT NULL
|
||||||
|
AND CurrentScan.cur_Port NOT IN ('', 'null')
|
||||||
|
)
|
||||||
|
AND {dev_parent_port_clause}
|
||||||
|
""",
|
||||||
|
(plugin_prefix, plugin_prefix, *parent_port_params),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
sql.execute(
|
||||||
|
f"""
|
||||||
|
UPDATE Devices
|
||||||
|
SET devParentPort = (
|
||||||
|
SELECT cur_Port
|
||||||
|
FROM CurrentScan
|
||||||
|
WHERE Devices.devMac = CurrentScan.cur_MAC
|
||||||
|
AND CurrentScan.cur_Port IS NOT NULL
|
||||||
|
AND CurrentScan.cur_Port NOT IN ('', 'null')
|
||||||
|
ORDER BY CurrentScan.cur_DateTime DESC
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
{parent_port_source_fragment}
|
||||||
|
WHERE {parent_port_empty_condition}
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM CurrentScan
|
||||||
|
WHERE Devices.devMac = CurrentScan.cur_MAC
|
||||||
|
AND CurrentScan.cur_Port IS NOT NULL
|
||||||
|
AND CurrentScan.cur_Port NOT IN ('', 'null')
|
||||||
|
)
|
||||||
|
AND {dev_parent_port_clause}
|
||||||
|
""",
|
||||||
|
parent_port_params,
|
||||||
|
)
|
||||||
|
|
||||||
# Update only devices with empty or NULL devParentMAC
|
# Update parent MAC
|
||||||
mylog("debug", "[Update Devices] - (if not empty) cur_NetworkNodeMAC -> devParentMAC")
|
mylog("debug", f"[Update Devices] - ({source_prefix}) cur_NetworkNodeMAC -> devParentMAC")
|
||||||
sql.execute("""UPDATE Devices
|
parent_mac_source_fragment = ", devParentMacSource = ?" if has_parent_mac_source else ""
|
||||||
|
parent_mac_params = (source_prefix,) if has_parent_mac_source else ()
|
||||||
|
|
||||||
|
if filter_by_scan_method:
|
||||||
|
sql.execute(
|
||||||
|
f"""
|
||||||
|
UPDATE Devices
|
||||||
SET devParentMAC = (
|
SET devParentMAC = (
|
||||||
SELECT cur_NetworkNodeMAC
|
SELECT cur_NetworkNodeMAC
|
||||||
FROM CurrentScan
|
FROM CurrentScan
|
||||||
WHERE Devices.devMac = CurrentScan.cur_MAC
|
WHERE Devices.devMac = CurrentScan.cur_MAC
|
||||||
|
AND CurrentScan.cur_ScanMethod = ?
|
||||||
|
AND CurrentScan.cur_NetworkNodeMAC IS NOT NULL
|
||||||
|
AND CurrentScan.cur_NetworkNodeMAC NOT IN ('', 'null')
|
||||||
|
ORDER BY CurrentScan.cur_DateTime DESC
|
||||||
|
LIMIT 1
|
||||||
)
|
)
|
||||||
WHERE
|
{parent_mac_source_fragment}
|
||||||
(devParentMAC IS NULL OR devParentMAC IN ("", "null", "(unknown)", "(Unknown)"))
|
WHERE {parent_mac_empty_condition}
|
||||||
AND
|
AND EXISTS (
|
||||||
EXISTS (
|
SELECT 1
|
||||||
SELECT 1
|
FROM CurrentScan
|
||||||
FROM CurrentScan
|
WHERE Devices.devMac = CurrentScan.cur_MAC
|
||||||
WHERE Devices.devMac = CurrentScan.cur_MAC
|
AND CurrentScan.cur_ScanMethod = ?
|
||||||
AND CurrentScan.cur_NetworkNodeMAC IS NOT NULL AND CurrentScan.cur_NetworkNodeMAC NOT IN ("", "null")
|
AND CurrentScan.cur_NetworkNodeMAC IS NOT NULL
|
||||||
)
|
AND CurrentScan.cur_NetworkNodeMAC NOT IN ('', 'null')
|
||||||
""")
|
)
|
||||||
|
AND {dev_parent_mac_clause}
|
||||||
|
""",
|
||||||
|
(plugin_prefix, plugin_prefix, *parent_mac_params),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
sql.execute(
|
||||||
|
f"""
|
||||||
|
UPDATE Devices
|
||||||
|
SET devParentMAC = (
|
||||||
|
SELECT cur_NetworkNodeMAC
|
||||||
|
FROM CurrentScan
|
||||||
|
WHERE Devices.devMac = CurrentScan.cur_MAC
|
||||||
|
AND CurrentScan.cur_NetworkNodeMAC IS NOT NULL
|
||||||
|
AND CurrentScan.cur_NetworkNodeMAC NOT IN ('', 'null')
|
||||||
|
ORDER BY CurrentScan.cur_DateTime DESC
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
{parent_mac_source_fragment}
|
||||||
|
WHERE {parent_mac_empty_condition}
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM CurrentScan
|
||||||
|
WHERE Devices.devMac = CurrentScan.cur_MAC
|
||||||
|
AND CurrentScan.cur_NetworkNodeMAC IS NOT NULL
|
||||||
|
AND CurrentScan.cur_NetworkNodeMAC NOT IN ('', 'null')
|
||||||
|
)
|
||||||
|
AND {dev_parent_mac_clause}
|
||||||
|
""",
|
||||||
|
parent_mac_params,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Update SSID
|
||||||
|
mylog("debug", f"[Update Devices] - ({source_prefix}) cur_SSID -> devSSID")
|
||||||
|
ssid_source_fragment = ", devSsidSource = ?" if has_ssid_source else ""
|
||||||
|
ssid_params = (source_prefix,) if has_ssid_source else ()
|
||||||
|
|
||||||
|
if filter_by_scan_method:
|
||||||
|
sql.execute(
|
||||||
|
f"""
|
||||||
|
UPDATE Devices
|
||||||
|
SET devSSID = (
|
||||||
|
SELECT cur_SSID
|
||||||
|
FROM CurrentScan
|
||||||
|
WHERE Devices.devMac = CurrentScan.cur_MAC
|
||||||
|
AND CurrentScan.cur_ScanMethod = ?
|
||||||
|
AND CurrentScan.cur_SSID IS NOT NULL
|
||||||
|
AND CurrentScan.cur_SSID NOT IN ('', 'null')
|
||||||
|
ORDER BY CurrentScan.cur_DateTime DESC
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
{ssid_source_fragment}
|
||||||
|
WHERE {ssid_empty_condition}
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM CurrentScan
|
||||||
|
WHERE Devices.devMac = CurrentScan.cur_MAC
|
||||||
|
AND CurrentScan.cur_ScanMethod = ?
|
||||||
|
AND CurrentScan.cur_SSID IS NOT NULL
|
||||||
|
AND CurrentScan.cur_SSID NOT IN ('', 'null')
|
||||||
|
)
|
||||||
|
AND {dev_ssid_clause}
|
||||||
|
""",
|
||||||
|
(plugin_prefix, plugin_prefix, *ssid_params),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
sql.execute(
|
||||||
|
f"""
|
||||||
|
UPDATE Devices
|
||||||
|
SET devSSID = (
|
||||||
|
SELECT cur_SSID
|
||||||
|
FROM CurrentScan
|
||||||
|
WHERE Devices.devMac = CurrentScan.cur_MAC
|
||||||
|
AND CurrentScan.cur_SSID IS NOT NULL
|
||||||
|
AND CurrentScan.cur_SSID NOT IN ('', 'null')
|
||||||
|
ORDER BY CurrentScan.cur_DateTime DESC
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
{ssid_source_fragment}
|
||||||
|
WHERE {ssid_empty_condition}
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM CurrentScan
|
||||||
|
WHERE Devices.devMac = CurrentScan.cur_MAC
|
||||||
|
AND CurrentScan.cur_SSID IS NOT NULL
|
||||||
|
AND CurrentScan.cur_SSID NOT IN ('', 'null')
|
||||||
|
)
|
||||||
|
AND {dev_ssid_clause}
|
||||||
|
""",
|
||||||
|
ssid_params,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Update Name
|
||||||
|
mylog("debug", f"[Update Devices] - ({source_prefix}) cur_Name -> devName")
|
||||||
|
name_source_fragment = ", devNameSource = ?" if has_name_source else ""
|
||||||
|
name_params = (source_prefix,) if has_name_source else ()
|
||||||
|
|
||||||
|
if filter_by_scan_method:
|
||||||
|
sql.execute(
|
||||||
|
f"""
|
||||||
|
UPDATE Devices
|
||||||
|
SET devName = (
|
||||||
|
SELECT cur_Name
|
||||||
|
FROM CurrentScan
|
||||||
|
WHERE cur_MAC = devMac
|
||||||
|
AND cur_ScanMethod = ?
|
||||||
|
AND cur_Name IS NOT NULL
|
||||||
|
AND cur_Name <> 'null'
|
||||||
|
AND cur_Name <> ''
|
||||||
|
ORDER BY cur_DateTime DESC
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
{name_source_fragment}
|
||||||
|
WHERE {name_empty_condition}
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM CurrentScan
|
||||||
|
WHERE cur_MAC = devMac
|
||||||
|
AND cur_ScanMethod = ?
|
||||||
|
AND cur_Name IS NOT NULL
|
||||||
|
AND cur_Name <> 'null'
|
||||||
|
AND cur_Name <> ''
|
||||||
|
)
|
||||||
|
AND {dev_name_clause}
|
||||||
|
""",
|
||||||
|
(plugin_prefix, plugin_prefix, *name_params),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
sql.execute(
|
||||||
|
f"""
|
||||||
|
UPDATE Devices
|
||||||
|
SET devName = (
|
||||||
|
SELECT cur_Name
|
||||||
|
FROM CurrentScan
|
||||||
|
WHERE cur_MAC = devMac
|
||||||
|
AND cur_Name IS NOT NULL
|
||||||
|
AND cur_Name <> 'null'
|
||||||
|
AND cur_Name <> ''
|
||||||
|
ORDER BY cur_DateTime DESC
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
{name_source_fragment}
|
||||||
|
WHERE {name_empty_condition}
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM CurrentScan
|
||||||
|
WHERE cur_MAC = devMac
|
||||||
|
AND cur_Name IS NOT NULL
|
||||||
|
AND cur_Name <> 'null'
|
||||||
|
AND cur_Name <> ''
|
||||||
|
)
|
||||||
|
AND {dev_name_clause}
|
||||||
|
""",
|
||||||
|
name_params,
|
||||||
|
)
|
||||||
|
|
||||||
# Update only devices with empty or NULL devSite
|
# Update only devices with empty or NULL devSite
|
||||||
mylog("debug", "[Update Devices] - (if not empty) cur_NetworkSite -> (if empty) devSite",)
|
mylog("debug", "[Update Devices] - (if not empty) cur_NetworkSite -> (if empty) devSite",)
|
||||||
@@ -168,23 +559,6 @@ def update_devices_data_from_scan(db):
|
|||||||
AND CurrentScan.cur_NetworkSite IS NOT NULL AND CurrentScan.cur_NetworkSite NOT IN ("", "null")
|
AND CurrentScan.cur_NetworkSite IS NOT NULL AND CurrentScan.cur_NetworkSite NOT IN ("", "null")
|
||||||
)""")
|
)""")
|
||||||
|
|
||||||
# Update only devices with empty or NULL devSSID
|
|
||||||
mylog("debug", "[Update Devices] - (if not empty) cur_SSID -> (if empty) devSSID")
|
|
||||||
sql.execute("""UPDATE Devices
|
|
||||||
SET devSSID = (
|
|
||||||
SELECT cur_SSID
|
|
||||||
FROM CurrentScan
|
|
||||||
WHERE Devices.devMac = CurrentScan.cur_MAC
|
|
||||||
)
|
|
||||||
WHERE
|
|
||||||
(devSSID IS NULL OR devSSID IN ("", "null"))
|
|
||||||
AND EXISTS (
|
|
||||||
SELECT 1
|
|
||||||
FROM CurrentScan
|
|
||||||
WHERE Devices.devMac = CurrentScan.cur_MAC
|
|
||||||
AND CurrentScan.cur_SSID IS NOT NULL AND CurrentScan.cur_SSID NOT IN ("", "null")
|
|
||||||
)""")
|
|
||||||
|
|
||||||
# Update only devices with empty or NULL devType
|
# Update only devices with empty or NULL devType
|
||||||
mylog("debug", "[Update Devices] - (if not empty) cur_Type -> (if empty) devType")
|
mylog("debug", "[Update Devices] - (if not empty) cur_Type -> (if empty) devType")
|
||||||
sql.execute("""UPDATE Devices
|
sql.execute("""UPDATE Devices
|
||||||
@@ -202,43 +576,48 @@ def update_devices_data_from_scan(db):
|
|||||||
AND CurrentScan.cur_Type IS NOT NULL AND CurrentScan.cur_Type NOT IN ("", "null")
|
AND CurrentScan.cur_Type IS NOT NULL AND CurrentScan.cur_Type NOT IN ("", "null")
|
||||||
)""")
|
)""")
|
||||||
|
|
||||||
# Update (unknown) or (name not found) Names if available
|
|
||||||
mylog("debug", "[Update Devices] - (if not empty) cur_Name -> (if empty) devName")
|
|
||||||
sql.execute(""" UPDATE Devices
|
|
||||||
SET devName = COALESCE((
|
|
||||||
SELECT cur_Name
|
|
||||||
FROM CurrentScan
|
|
||||||
WHERE cur_MAC = devMac
|
|
||||||
AND cur_Name IS NOT NULL
|
|
||||||
AND cur_Name <> 'null'
|
|
||||||
AND cur_Name <> ''
|
|
||||||
), devName)
|
|
||||||
WHERE (devName IN ('(unknown)', '(name not found)', '')
|
|
||||||
OR devName IS NULL)
|
|
||||||
AND EXISTS (
|
|
||||||
SELECT 1
|
|
||||||
FROM CurrentScan
|
|
||||||
WHERE cur_MAC = devMac
|
|
||||||
AND cur_Name IS NOT NULL
|
|
||||||
AND cur_Name <> 'null'
|
|
||||||
AND cur_Name <> ''
|
|
||||||
) """)
|
|
||||||
|
|
||||||
# Update VENDORS
|
# Update VENDORS
|
||||||
recordsToUpdate = []
|
recordsToUpdate = []
|
||||||
query = """SELECT * FROM Devices
|
vendor_settings = get_plugin_authoritative_settings("VNDRPDT")
|
||||||
WHERE devVendor IS NULL OR devVendor IN ("", "null", "(unknown)", "(Unknown)")
|
vendor_clause = (
|
||||||
"""
|
get_overwrite_sql_clause("devVendor", "devVendorSource", vendor_settings)
|
||||||
|
if has_column("devVendorSource")
|
||||||
|
else "1=1"
|
||||||
|
)
|
||||||
|
vendor_is_set_always = "devVendor" in vendor_settings.get("set_always", [])
|
||||||
|
|
||||||
|
if vendor_is_set_always:
|
||||||
|
query = f"""SELECT * FROM Devices
|
||||||
|
WHERE {vendor_clause}
|
||||||
|
"""
|
||||||
|
else:
|
||||||
|
query = f"""SELECT * FROM Devices
|
||||||
|
WHERE (devVendor IS NULL OR devVendor IN ("", "null", "(unknown)", "(Unknown)"))
|
||||||
|
AND {vendor_clause}
|
||||||
|
"""
|
||||||
|
|
||||||
for device in sql.execute(query):
|
for device in sql.execute(query):
|
||||||
vendor = query_MAC_vendor(device["devMac"])
|
vendor = query_MAC_vendor(device["devMac"])
|
||||||
if vendor != -1 and vendor != -2:
|
if vendor != -1 and vendor != -2:
|
||||||
recordsToUpdate.append([vendor, device["devMac"]])
|
recordsToUpdate.append([vendor, "VNDRPDT", device["devMac"]])
|
||||||
|
|
||||||
if len(recordsToUpdate) > 0:
|
if len(recordsToUpdate) > 0:
|
||||||
sql.executemany(
|
if has_column("devVendorSource"):
|
||||||
"UPDATE Devices SET devVendor = ? WHERE devMac = ? ", recordsToUpdate
|
sql.executemany(
|
||||||
)
|
f"""UPDATE Devices
|
||||||
|
SET devVendor = ?,
|
||||||
|
devVendorSource = ?
|
||||||
|
WHERE devMac = ?
|
||||||
|
AND {vendor_clause}""",
|
||||||
|
recordsToUpdate,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
sql.executemany(
|
||||||
|
"""UPDATE Devices
|
||||||
|
SET devVendor = ?
|
||||||
|
WHERE devMac = ?""",
|
||||||
|
[(row[0], row[2]) for row in recordsToUpdate],
|
||||||
|
)
|
||||||
|
|
||||||
# Update devPresentLastScan based on NICs presence
|
# Update devPresentLastScan based on NICs presence
|
||||||
update_devPresentLastScan_based_on_nics(db)
|
update_devPresentLastScan_based_on_nics(db)
|
||||||
@@ -524,12 +903,20 @@ def create_new_devices(db):
|
|||||||
cur_Type,
|
cur_Type,
|
||||||
) = row
|
) = row
|
||||||
|
|
||||||
|
# Preserve raw values to determine source attribution
|
||||||
|
raw_name = str(cur_Name).strip() if cur_Name else ""
|
||||||
|
raw_vendor = str(cur_Vendor).strip() if cur_Vendor else ""
|
||||||
|
raw_ip = str(cur_IP).strip() if cur_IP else ""
|
||||||
|
raw_ssid = str(cur_SSID).strip() if cur_SSID else ""
|
||||||
|
raw_parent_mac = cur_NetworkNodeMAC.strip() if cur_NetworkNodeMAC else ""
|
||||||
|
raw_parent_port = str(cur_PORT).strip() if cur_PORT else ""
|
||||||
|
|
||||||
# Handle NoneType
|
# Handle NoneType
|
||||||
cur_Name = str(cur_Name).strip() if cur_Name else "(unknown)"
|
cur_Name = raw_name if raw_name else "(unknown)"
|
||||||
cur_Type = (
|
cur_Type = (
|
||||||
str(cur_Type).strip() if cur_Type else get_setting_value("NEWDEV_devType")
|
str(cur_Type).strip() if cur_Type else get_setting_value("NEWDEV_devType")
|
||||||
)
|
)
|
||||||
cur_NetworkNodeMAC = cur_NetworkNodeMAC.strip() if cur_NetworkNodeMAC else ""
|
cur_NetworkNodeMAC = raw_parent_mac
|
||||||
cur_NetworkNodeMAC = (
|
cur_NetworkNodeMAC = (
|
||||||
cur_NetworkNodeMAC
|
cur_NetworkNodeMAC
|
||||||
if cur_NetworkNodeMAC and cur_MAC != "Internet"
|
if cur_NetworkNodeMAC and cur_MAC != "Internet"
|
||||||
@@ -546,7 +933,7 @@ def create_new_devices(db):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Derive primary IP family values
|
# Derive primary IP family values
|
||||||
cur_IP = str(cur_IP).strip() if cur_IP else ""
|
cur_IP = raw_ip
|
||||||
cur_IP_normalized = check_IP_format(cur_IP) if ":" not in cur_IP else cur_IP
|
cur_IP_normalized = check_IP_format(cur_IP) if ":" not in cur_IP else cur_IP
|
||||||
|
|
||||||
# Validate IPv6 addresses using format_ip_long for consistency (do not store integer result)
|
# Validate IPv6 addresses using format_ip_long for consistency (do not store integer result)
|
||||||
@@ -558,6 +945,33 @@ def create_new_devices(db):
|
|||||||
primary_ipv4 = cur_IP_normalized if cur_IP_normalized and ":" not in cur_IP_normalized else ""
|
primary_ipv4 = cur_IP_normalized if cur_IP_normalized and ":" not in cur_IP_normalized else ""
|
||||||
primary_ipv6 = cur_IP_normalized if cur_IP_normalized and ":" in cur_IP_normalized else ""
|
primary_ipv6 = cur_IP_normalized if cur_IP_normalized and ":" in cur_IP_normalized else ""
|
||||||
|
|
||||||
|
plugin_prefix = str(cur_ScanMethod).strip() if cur_ScanMethod else "NEWDEV"
|
||||||
|
|
||||||
|
dev_mac_source = get_source_for_field_update_with_value(
|
||||||
|
"devMac", plugin_prefix, cur_MAC, is_user_override=False
|
||||||
|
)
|
||||||
|
dev_name_source = get_source_for_field_update_with_value(
|
||||||
|
"devName", plugin_prefix, raw_name, is_user_override=False
|
||||||
|
)
|
||||||
|
dev_vendor_source = get_source_for_field_update_with_value(
|
||||||
|
"devVendor", plugin_prefix, raw_vendor, is_user_override=False
|
||||||
|
)
|
||||||
|
dev_last_ip_source = get_source_for_field_update_with_value(
|
||||||
|
"devLastIP", plugin_prefix, cur_IP_normalized, is_user_override=False
|
||||||
|
)
|
||||||
|
dev_ssid_source = get_source_for_field_update_with_value(
|
||||||
|
"devSSID", plugin_prefix, raw_ssid, is_user_override=False
|
||||||
|
)
|
||||||
|
dev_parent_mac_source = get_source_for_field_update_with_value(
|
||||||
|
"devParentMAC", plugin_prefix, raw_parent_mac, is_user_override=False
|
||||||
|
)
|
||||||
|
dev_parent_port_source = get_source_for_field_update_with_value(
|
||||||
|
"devParentPort", plugin_prefix, raw_parent_port, is_user_override=False
|
||||||
|
)
|
||||||
|
dev_parent_rel_type_source = "NEWDEV"
|
||||||
|
dev_fqdn_source = "NEWDEV"
|
||||||
|
dev_vlan_source = "NEWDEV"
|
||||||
|
|
||||||
# Preparing the individual insert statement
|
# Preparing the individual insert statement
|
||||||
sqlQuery = f"""INSERT OR IGNORE INTO Devices
|
sqlQuery = f"""INSERT OR IGNORE INTO Devices
|
||||||
(
|
(
|
||||||
@@ -577,6 +991,16 @@ def create_new_devices(db):
|
|||||||
devSSID,
|
devSSID,
|
||||||
devType,
|
devType,
|
||||||
devSourcePlugin,
|
devSourcePlugin,
|
||||||
|
devMacSource,
|
||||||
|
devNameSource,
|
||||||
|
devFqdnSource,
|
||||||
|
devLastIpSource,
|
||||||
|
devVendorSource,
|
||||||
|
devSsidSource,
|
||||||
|
devParentMacSource,
|
||||||
|
devParentPortSource,
|
||||||
|
devParentRelTypeSource,
|
||||||
|
devVlanSource,
|
||||||
{newDevColumns}
|
{newDevColumns}
|
||||||
)
|
)
|
||||||
VALUES
|
VALUES
|
||||||
@@ -597,6 +1021,16 @@ def create_new_devices(db):
|
|||||||
'{sanitize_SQL_input(cur_SSID)}',
|
'{sanitize_SQL_input(cur_SSID)}',
|
||||||
'{sanitize_SQL_input(cur_Type)}',
|
'{sanitize_SQL_input(cur_Type)}',
|
||||||
'{sanitize_SQL_input(cur_ScanMethod)}',
|
'{sanitize_SQL_input(cur_ScanMethod)}',
|
||||||
|
'{sanitize_SQL_input(dev_mac_source)}',
|
||||||
|
'{sanitize_SQL_input(dev_name_source)}',
|
||||||
|
'{sanitize_SQL_input(dev_fqdn_source)}',
|
||||||
|
'{sanitize_SQL_input(dev_last_ip_source)}',
|
||||||
|
'{sanitize_SQL_input(dev_vendor_source)}',
|
||||||
|
'{sanitize_SQL_input(dev_ssid_source)}',
|
||||||
|
'{sanitize_SQL_input(dev_parent_mac_source)}',
|
||||||
|
'{sanitize_SQL_input(dev_parent_port_source)}',
|
||||||
|
'{sanitize_SQL_input(dev_parent_rel_type_source)}',
|
||||||
|
'{sanitize_SQL_input(dev_vlan_source)}',
|
||||||
{newDevDefaults}
|
{newDevDefaults}
|
||||||
)"""
|
)"""
|
||||||
|
|
||||||
@@ -709,7 +1143,8 @@ def update_devices_names(pm):
|
|||||||
If False, resolves only FQDN.
|
If False, resolves only FQDN.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
recordsToUpdate (list): List of [newName, newFQDN, devMac] or [newFQDN, devMac] for DB update.
|
recordsToUpdate (list): List of
|
||||||
|
[newName, nameSource, newFQDN, fqdnSource, devMac] or [newFQDN, fqdnSource, devMac].
|
||||||
recordsNotFound (list): List of [nameNotFound, devMac] for DB update.
|
recordsNotFound (list): List of [nameNotFound, devMac] for DB update.
|
||||||
foundStats (dict): Number of successes per strategy.
|
foundStats (dict): Number of successes per strategy.
|
||||||
notFound (int): Number of devices not resolved.
|
notFound (int): Number of devices not resolved.
|
||||||
@@ -738,9 +1173,9 @@ def update_devices_names(pm):
|
|||||||
foundStats[label] += 1
|
foundStats[label] += 1
|
||||||
|
|
||||||
if resolve_both_name_and_fqdn:
|
if resolve_both_name_and_fqdn:
|
||||||
recordsToUpdate.append([newName, newFQDN, device["devMac"]])
|
recordsToUpdate.append([newName, label, newFQDN, label, device["devMac"]])
|
||||||
else:
|
else:
|
||||||
recordsToUpdate.append([newFQDN, device["devMac"]])
|
recordsToUpdate.append([newFQDN, label, device["devMac"]])
|
||||||
break
|
break
|
||||||
|
|
||||||
# If no name was resolved, queue device for "(name not found)" update
|
# If no name was resolved, queue device for "(name not found)" update
|
||||||
@@ -768,13 +1203,51 @@ def update_devices_names(pm):
|
|||||||
|
|
||||||
# Apply updates to database
|
# Apply updates to database
|
||||||
sql.executemany(
|
sql.executemany(
|
||||||
"UPDATE Devices SET devName = ? WHERE devMac = ?", recordsNotFound
|
"""UPDATE Devices
|
||||||
)
|
SET devName = CASE
|
||||||
sql.executemany(
|
WHEN COALESCE(devNameSource, '') IN ('USER', 'LOCKED') THEN devName
|
||||||
"UPDATE Devices SET devName = ?, devFQDN = ? WHERE devMac = ?",
|
ELSE ?
|
||||||
recordsToUpdate,
|
END
|
||||||
|
WHERE devMac = ?
|
||||||
|
AND COALESCE(devNameSource, '') IN ('', 'NEWDEV')""",
|
||||||
|
recordsNotFound,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
records_by_plugin = {}
|
||||||
|
for entry in recordsToUpdate:
|
||||||
|
records_by_plugin.setdefault(entry[1], []).append(entry)
|
||||||
|
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
|
||||||
# --- Step 2: Optionally refresh FQDN for all devices ---
|
# --- Step 2: Optionally refresh FQDN for all devices ---
|
||||||
if get_setting_value("REFRESH_FQDN"):
|
if get_setting_value("REFRESH_FQDN"):
|
||||||
allDevices = device_handler.getAll()
|
allDevices = device_handler.getAll()
|
||||||
@@ -791,10 +1264,30 @@ def update_devices_names(pm):
|
|||||||
mylog("verbose", f"[Update FQDN] Names Found (DIGSCAN/AVAHISCAN/NSLOOKUP/NBTSCAN): {len(recordsToUpdate)}({res_string})",)
|
mylog("verbose", f"[Update FQDN] Names Found (DIGSCAN/AVAHISCAN/NSLOOKUP/NBTSCAN): {len(recordsToUpdate)}({res_string})",)
|
||||||
mylog("verbose", f"[Update FQDN] Names Not Found : {notFound}")
|
mylog("verbose", f"[Update FQDN] Names Not Found : {notFound}")
|
||||||
|
|
||||||
# Apply FQDN-only updates
|
records_by_plugin = {}
|
||||||
sql.executemany(
|
for entry in recordsToUpdate:
|
||||||
"UPDATE Devices SET devFQDN = ? WHERE devMac = ?", recordsToUpdate
|
records_by_plugin.setdefault(entry[1], []).append(entry)
|
||||||
)
|
|
||||||
|
for plugin_label, plugin_records in records_by_plugin.items():
|
||||||
|
plugin_settings = get_plugin_authoritative_settings(plugin_label)
|
||||||
|
fqdn_clause = get_overwrite_sql_clause(
|
||||||
|
"devFQDN", "devFqdnSource", plugin_settings
|
||||||
|
)
|
||||||
|
|
||||||
|
# Apply FQDN-only updates
|
||||||
|
sql.executemany(
|
||||||
|
f"""UPDATE Devices
|
||||||
|
SET devFQDN = CASE
|
||||||
|
WHEN {fqdn_clause} THEN ?
|
||||||
|
ELSE devFQDN
|
||||||
|
END,
|
||||||
|
devFqdnSource = CASE
|
||||||
|
WHEN {fqdn_clause} THEN ?
|
||||||
|
ELSE devFqdnSource
|
||||||
|
END
|
||||||
|
WHERE devMac = ?""",
|
||||||
|
plugin_records,
|
||||||
|
)
|
||||||
|
|
||||||
# Commit all database changes
|
# Commit all database changes
|
||||||
pm.db.commitDB()
|
pm.db.commitDB()
|
||||||
|
|||||||
@@ -2,11 +2,9 @@
|
|||||||
Unit tests for authoritative field update handler.
|
Unit tests for authoritative field update handler.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from server.db.authoritative_handler import (
|
from server.db.authoritative_handler import (
|
||||||
can_overwrite_field,
|
can_overwrite_field,
|
||||||
get_source_for_field_update,
|
get_source_for_field_update_with_value,
|
||||||
FIELD_SOURCE_MAP,
|
FIELD_SOURCE_MAP,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -75,17 +73,52 @@ class TestCanOverwriteField:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestGetSourceForFieldUpdate:
|
class TestGetSourceForFieldUpdateWithValue:
|
||||||
"""Test source value determination for field updates."""
|
"""Test source value determination with value-based normalization."""
|
||||||
|
|
||||||
def test_user_override_sets_user_source(self):
|
def test_user_override_sets_user_source(self):
|
||||||
"""User override should set USER source."""
|
assert (
|
||||||
assert get_source_for_field_update("devName", "UNIFIAPI", is_user_override=True) == "USER"
|
get_source_for_field_update_with_value(
|
||||||
|
"devName", "UNIFIAPI", "Device", is_user_override=True
|
||||||
|
)
|
||||||
|
== "USER"
|
||||||
|
)
|
||||||
|
|
||||||
def test_plugin_update_sets_plugin_prefix(self):
|
def test_plugin_update_sets_plugin_prefix(self):
|
||||||
"""Plugin update should set plugin prefix as source."""
|
assert (
|
||||||
assert get_source_for_field_update("devName", "UNIFIAPI", is_user_override=False) == "UNIFIAPI"
|
get_source_for_field_update_with_value(
|
||||||
assert get_source_for_field_update("devLastIP", "ARPSCAN", is_user_override=False) == "ARPSCAN"
|
"devName", "UNIFIAPI", "Device", is_user_override=False
|
||||||
|
)
|
||||||
|
== "UNIFIAPI"
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
get_source_for_field_update_with_value(
|
||||||
|
"devLastIP", "ARPSCAN", "192.168.1.1", is_user_override=False
|
||||||
|
)
|
||||||
|
== "ARPSCAN"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_empty_or_unknown_values_return_newdev(self):
|
||||||
|
assert (
|
||||||
|
get_source_for_field_update_with_value(
|
||||||
|
"devName", "ARPSCAN", "", is_user_override=False
|
||||||
|
)
|
||||||
|
== "NEWDEV"
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
get_source_for_field_update_with_value(
|
||||||
|
"devName", "ARPSCAN", "(unknown)", is_user_override=False
|
||||||
|
)
|
||||||
|
== "NEWDEV"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_non_empty_value_sets_plugin_prefix(self):
|
||||||
|
assert (
|
||||||
|
get_source_for_field_update_with_value(
|
||||||
|
"devVendor", "ARPSCAN", "Acme", is_user_override=False
|
||||||
|
)
|
||||||
|
== "ARPSCAN"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestFieldSourceMapping:
|
class TestFieldSourceMapping:
|
||||||
|
|||||||
@@ -83,6 +83,34 @@ def scan_db():
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
CREATE TABLE Events (
|
||||||
|
eve_MAC TEXT,
|
||||||
|
eve_IP TEXT,
|
||||||
|
eve_DateTime TEXT,
|
||||||
|
eve_EventType TEXT,
|
||||||
|
eve_AdditionalInfo TEXT,
|
||||||
|
eve_PendingAlertEmail INTEGER
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
CREATE TABLE Sessions (
|
||||||
|
ses_MAC TEXT,
|
||||||
|
ses_IP TEXT,
|
||||||
|
ses_EventTypeConnection TEXT,
|
||||||
|
ses_DateTimeConnection TEXT,
|
||||||
|
ses_EventTypeDisconnection TEXT,
|
||||||
|
ses_DateTimeDisconnection TEXT,
|
||||||
|
ses_StillConnected INTEGER,
|
||||||
|
ses_AdditionalInfo TEXT
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
yield conn
|
yield conn
|
||||||
conn.close()
|
conn.close()
|
||||||
@@ -109,6 +137,197 @@ def mock_device_handlers():
|
|||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def scan_db_for_new_devices():
|
||||||
|
"""Create an in-memory SQLite database for create_new_devices tests."""
|
||||||
|
conn = sqlite3.connect(":memory:")
|
||||||
|
conn.row_factory = sqlite3.Row
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
CREATE TABLE Devices (
|
||||||
|
devMac TEXT PRIMARY KEY,
|
||||||
|
devName TEXT,
|
||||||
|
devVendor TEXT,
|
||||||
|
devLastIP TEXT,
|
||||||
|
devPrimaryIPv4 TEXT,
|
||||||
|
devPrimaryIPv6 TEXT,
|
||||||
|
devFirstConnection TEXT,
|
||||||
|
devLastConnection TEXT,
|
||||||
|
devSyncHubNode TEXT,
|
||||||
|
devGUID TEXT,
|
||||||
|
devParentMAC TEXT,
|
||||||
|
devParentPort TEXT,
|
||||||
|
devSite TEXT,
|
||||||
|
devSSID TEXT,
|
||||||
|
devType TEXT,
|
||||||
|
devSourcePlugin TEXT,
|
||||||
|
devMacSource TEXT,
|
||||||
|
devNameSource TEXT,
|
||||||
|
devFqdnSource TEXT,
|
||||||
|
devLastIpSource TEXT,
|
||||||
|
devVendorSource TEXT,
|
||||||
|
devSsidSource TEXT,
|
||||||
|
devParentMacSource TEXT,
|
||||||
|
devParentPortSource TEXT,
|
||||||
|
devParentRelTypeSource TEXT,
|
||||||
|
devVlanSource TEXT,
|
||||||
|
devAlertEvents INTEGER,
|
||||||
|
devAlertDown INTEGER,
|
||||||
|
devPresentLastScan INTEGER,
|
||||||
|
devIsArchived INTEGER,
|
||||||
|
devIsNew INTEGER,
|
||||||
|
devSkipRepeated INTEGER,
|
||||||
|
devScan INTEGER,
|
||||||
|
devOwner TEXT,
|
||||||
|
devFavorite INTEGER,
|
||||||
|
devGroup TEXT,
|
||||||
|
devComments TEXT,
|
||||||
|
devLogEvents INTEGER,
|
||||||
|
devLocation TEXT,
|
||||||
|
devCustomProps TEXT,
|
||||||
|
devParentRelType TEXT,
|
||||||
|
devReqNicsOnline INTEGER
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
CREATE TABLE CurrentScan (
|
||||||
|
cur_MAC TEXT,
|
||||||
|
cur_Name TEXT,
|
||||||
|
cur_Vendor TEXT,
|
||||||
|
cur_ScanMethod TEXT,
|
||||||
|
cur_IP TEXT,
|
||||||
|
cur_SyncHubNodeName TEXT,
|
||||||
|
cur_NetworkNodeMAC TEXT,
|
||||||
|
cur_PORT TEXT,
|
||||||
|
cur_NetworkSite TEXT,
|
||||||
|
cur_SSID TEXT,
|
||||||
|
cur_Type TEXT
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
CREATE TABLE Events (
|
||||||
|
eve_MAC TEXT,
|
||||||
|
eve_IP TEXT,
|
||||||
|
eve_DateTime TEXT,
|
||||||
|
eve_EventType TEXT,
|
||||||
|
eve_AdditionalInfo TEXT,
|
||||||
|
eve_PendingAlertEmail INTEGER
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
CREATE TABLE Sessions (
|
||||||
|
ses_MAC TEXT,
|
||||||
|
ses_IP TEXT,
|
||||||
|
ses_EventTypeConnection TEXT,
|
||||||
|
ses_DateTimeConnection TEXT,
|
||||||
|
ses_EventTypeDisconnection TEXT,
|
||||||
|
ses_DateTimeDisconnection TEXT,
|
||||||
|
ses_StillConnected INTEGER,
|
||||||
|
ses_AdditionalInfo TEXT
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
yield conn
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_new_devices_sets_sources(scan_db_for_new_devices):
|
||||||
|
"""New device insert initializes source fields from scan method."""
|
||||||
|
cur = scan_db_for_new_devices.cursor()
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO CurrentScan (
|
||||||
|
cur_MAC, cur_Name, cur_Vendor, cur_ScanMethod, cur_IP,
|
||||||
|
cur_SyncHubNodeName, cur_NetworkNodeMAC, cur_PORT,
|
||||||
|
cur_NetworkSite, cur_SSID, cur_Type
|
||||||
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
""",
|
||||||
|
(
|
||||||
|
"AA:BB:CC:DD:EE:10",
|
||||||
|
"DeviceOne",
|
||||||
|
"AcmeVendor",
|
||||||
|
"ARPSCAN",
|
||||||
|
"192.168.1.10",
|
||||||
|
"",
|
||||||
|
"11:22:33:44:55:66",
|
||||||
|
"1",
|
||||||
|
"",
|
||||||
|
"MyWifi",
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
scan_db_for_new_devices.commit()
|
||||||
|
|
||||||
|
settings = {
|
||||||
|
"NEWDEV_devType": "default-type",
|
||||||
|
"NEWDEV_devParentMAC": "FF:FF:FF:FF:FF:FF",
|
||||||
|
"NEWDEV_devOwner": "owner",
|
||||||
|
"NEWDEV_devGroup": "group",
|
||||||
|
"NEWDEV_devComments": "",
|
||||||
|
"NEWDEV_devLocation": "",
|
||||||
|
"NEWDEV_devCustomProps": "",
|
||||||
|
"NEWDEV_devParentRelType": "uplink",
|
||||||
|
"SYNC_node_name": "SYNCNODE",
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_setting_value_side_effect(key):
|
||||||
|
return settings.get(key, "")
|
||||||
|
|
||||||
|
db = Mock()
|
||||||
|
db.sql_connection = scan_db_for_new_devices
|
||||||
|
db.sql = cur
|
||||||
|
db.commitDB = scan_db_for_new_devices.commit
|
||||||
|
|
||||||
|
with patch.multiple(
|
||||||
|
device_handling,
|
||||||
|
get_setting_value=Mock(side_effect=get_setting_value_side_effect),
|
||||||
|
safe_int=Mock(return_value=0),
|
||||||
|
):
|
||||||
|
device_handling.create_new_devices(db)
|
||||||
|
|
||||||
|
row = cur.execute(
|
||||||
|
"""
|
||||||
|
SELECT
|
||||||
|
devMacSource,
|
||||||
|
devNameSource,
|
||||||
|
devVendorSource,
|
||||||
|
devLastIpSource,
|
||||||
|
devSsidSource,
|
||||||
|
devParentMacSource,
|
||||||
|
devParentPortSource,
|
||||||
|
devParentRelTypeSource,
|
||||||
|
devFqdnSource,
|
||||||
|
devVlanSource
|
||||||
|
FROM Devices WHERE devMac = ?
|
||||||
|
""",
|
||||||
|
("AA:BB:CC:DD:EE:10",),
|
||||||
|
).fetchone()
|
||||||
|
|
||||||
|
assert row["devMacSource"] == "ARPSCAN"
|
||||||
|
assert row["devNameSource"] == "ARPSCAN"
|
||||||
|
assert row["devVendorSource"] == "ARPSCAN"
|
||||||
|
assert row["devLastIpSource"] == "ARPSCAN"
|
||||||
|
assert row["devSsidSource"] == "ARPSCAN"
|
||||||
|
assert row["devParentMacSource"] == "ARPSCAN"
|
||||||
|
assert row["devParentPortSource"] == "ARPSCAN"
|
||||||
|
assert row["devParentRelTypeSource"] == "NEWDEV"
|
||||||
|
assert row["devFqdnSource"] == "NEWDEV"
|
||||||
|
assert row["devVlanSource"] == "NEWDEV"
|
||||||
|
|
||||||
|
|
||||||
def test_scan_updates_newdev_device_name(scan_db, mock_device_handlers):
|
def test_scan_updates_newdev_device_name(scan_db, mock_device_handlers):
|
||||||
"""Scanner discovers name for device with NEWDEV source."""
|
"""Scanner discovers name for device with NEWDEV source."""
|
||||||
cur = scan_db.cursor()
|
cur = scan_db.cursor()
|
||||||
|
|||||||
Reference in New Issue
Block a user