From 81202ce07e688555e43e57256d2ca6819e027552 Mon Sep 17 00:00:00 2001 From: jokob-sk Date: Tue, 3 Feb 2026 20:40:11 +1100 Subject: [PATCH] name resolution config clean up, authoritative fields fixes for none values, css fixes Signed-off-by: jokob-sk --- front/css/app.css | 11 +---- front/multiEditCore.php | 18 +++---- front/plugins/avahi_scan/config.json | 62 +++++++++++++++++++++++++ front/plugins/dig_scan/config.json | 62 +++++++++++++++++++++++++ front/plugins/nbtscan_scan/config.json | 62 +++++++++++++++++++++++++ front/plugins/nslookup_scan/config.json | 62 +++++++++++++++++++++++++ server/database.py | 21 --------- server/db/authoritative_handler.py | 22 +++++---- server/db/db_upgrade.py | 1 + server/scan/device_handling.py | 50 +++++++++----------- server/scan/session_events.py | 2 +- 11 files changed, 294 insertions(+), 79 deletions(-) diff --git a/front/css/app.css b/front/css/app.css index a3183087..1f665841 100755 --- a/front/css/app.css +++ b/front/css/app.css @@ -2249,9 +2249,8 @@ textarea[readonly], .red-hover-border:hover { - border-color: red !important; - border-width: 1px; - border-style: solid; + border-color: #ff0000 !important; + box-shadow: #ff0000; } @@ -2260,12 +2259,6 @@ textarea[readonly], background-color: red !important; } -#multi-edit-form .form-group -{ - height: 45px; - -} - .top-left-logo { height:35px; diff --git a/front/multiEditCore.php b/front/multiEditCore.php index e3f4c5e3..4484ff8a 100755 --- a/front/multiEditCore.php +++ b/front/multiEditCore.php @@ -59,22 +59,22 @@
-
- +
+
-
+
-
- +
+
-
+
-
- +
+
-
+
diff --git a/front/plugins/avahi_scan/config.json b/front/plugins/avahi_scan/config.json index 770e2541..d588e64d 100755 --- a/front/plugins/avahi_scan/config.json +++ b/front/plugins/avahi_scan/config.json @@ -225,6 +225,68 @@ "string": "Maximum time per device scan in seconds to wait for the script to finish. If this time is exceeded the script is aborted." } ] + }, + { + "function": "SET_ALWAYS", + "type": { + "dataType": "array", + "elements": [ + { + "elementType": "select", + "elementOptions": [{ "multiple": "true", "ordeable": "true"}], + "transformers": [] + } + ] + }, + "default_value": ["devFQDN"], + "options": [ + "devFQDN", + "devName" + ], + "localized": ["name", "description"], + "name": [ + { + "language_code": "en_us", + "string": "Set always columns" + } + ], + "description": [ + { + "language_code": "en_us", + "string": "These columns are treated as authoritative and will overwrite existing values, including those set by other plugins, unless the current value was explicitly set by the user (Source = USER or Source = LOCKED)." + } + ] + }, + { + "function": "SET_EMPTY", + "type": { + "dataType": "array", + "elements": [ + { + "elementType": "select", + "elementOptions": [{ "multiple": "true", "ordeable": "true" }], + "transformers": [] + } + ] + }, + "default_value": [], + "options": [ + "devFQDN", + "devName" + ], + "localized": ["name", "description"], + "name": [ + { + "language_code": "en_us", + "string": "Set empty columns" + } + ], + "description": [ + { + "language_code": "en_us", + "string": "These columns are only overwritten if they are empty (NULL / empty string) or if their Source is set to NEWDEV" + } + ] } ], "database_column_definitions": [ diff --git a/front/plugins/dig_scan/config.json b/front/plugins/dig_scan/config.json index 7a4d2820..8729f9a5 100755 --- a/front/plugins/dig_scan/config.json +++ b/front/plugins/dig_scan/config.json @@ -233,6 +233,68 @@ "string": "Maximale Zeit in Sekunden, die auf den Abschluss des Skripts gewartet werden soll. Bei Überschreitung dieser Zeit wird das Skript abgebrochen." } ] + }, + { + "function": "SET_ALWAYS", + "type": { + "dataType": "array", + "elements": [ + { + "elementType": "select", + "elementOptions": [{ "multiple": "true", "ordeable": "true"}], + "transformers": [] + } + ] + }, + "default_value": ["devFQDN"], + "options": [ + "devFQDN", + "devName" + ], + "localized": ["name", "description"], + "name": [ + { + "language_code": "en_us", + "string": "Set always columns" + } + ], + "description": [ + { + "language_code": "en_us", + "string": "These columns are treated as authoritative and will overwrite existing values, including those set by other plugins, unless the current value was explicitly set by the user (Source = USER or Source = LOCKED)." + } + ] + }, + { + "function": "SET_EMPTY", + "type": { + "dataType": "array", + "elements": [ + { + "elementType": "select", + "elementOptions": [{ "multiple": "true", "ordeable": "true" }], + "transformers": [] + } + ] + }, + "default_value": [], + "options": [ + "devFQDN", + "devName" + ], + "localized": ["name", "description"], + "name": [ + { + "language_code": "en_us", + "string": "Set empty columns" + } + ], + "description": [ + { + "language_code": "en_us", + "string": "These columns are only overwritten if they are empty (NULL / empty string) or if their Source is set to NEWDEV" + } + ] } ], "database_column_definitions": [ diff --git a/front/plugins/nbtscan_scan/config.json b/front/plugins/nbtscan_scan/config.json index b46ae793..48b8a589 100755 --- a/front/plugins/nbtscan_scan/config.json +++ b/front/plugins/nbtscan_scan/config.json @@ -233,6 +233,68 @@ "string": "Maximale Zeit in Sekunden, die auf den Abschluss des Skripts gewartet werden soll. Bei Überschreitung dieser Zeit wird das Skript abgebrochen." } ] + }, + { + "function": "SET_ALWAYS", + "type": { + "dataType": "array", + "elements": [ + { + "elementType": "select", + "elementOptions": [{ "multiple": "true", "ordeable": "true"}], + "transformers": [] + } + ] + }, + "default_value": ["devFQDN"], + "options": [ + "devFQDN", + "devName" + ], + "localized": ["name", "description"], + "name": [ + { + "language_code": "en_us", + "string": "Set always columns" + } + ], + "description": [ + { + "language_code": "en_us", + "string": "These columns are treated as authoritative and will overwrite existing values, including those set by other plugins, unless the current value was explicitly set by the user (Source = USER or Source = LOCKED)." + } + ] + }, + { + "function": "SET_EMPTY", + "type": { + "dataType": "array", + "elements": [ + { + "elementType": "select", + "elementOptions": [{ "multiple": "true", "ordeable": "true" }], + "transformers": [] + } + ] + }, + "default_value": [], + "options": [ + "devFQDN", + "devName" + ], + "localized": ["name", "description"], + "name": [ + { + "language_code": "en_us", + "string": "Set empty columns" + } + ], + "description": [ + { + "language_code": "en_us", + "string": "These columns are only overwritten if they are empty (NULL / empty string) or if their Source is set to NEWDEV" + } + ] } ], "database_column_definitions": [ diff --git a/front/plugins/nslookup_scan/config.json b/front/plugins/nslookup_scan/config.json index 1589e666..6bf444d0 100755 --- a/front/plugins/nslookup_scan/config.json +++ b/front/plugins/nslookup_scan/config.json @@ -233,6 +233,68 @@ "string": "Maximale Zeit in Sekunden, die auf den Abschluss des Skripts gewartet werden soll. Bei Überschreitung dieser Zeit wird das Skript abgebrochen." } ] + }, + { + "function": "SET_ALWAYS", + "type": { + "dataType": "array", + "elements": [ + { + "elementType": "select", + "elementOptions": [{ "multiple": "true", "ordeable": "true"}], + "transformers": [] + } + ] + }, + "default_value": ["devFQDN"], + "options": [ + "devFQDN", + "devName" + ], + "localized": ["name", "description"], + "name": [ + { + "language_code": "en_us", + "string": "Set always columns" + } + ], + "description": [ + { + "language_code": "en_us", + "string": "These columns are treated as authoritative and will overwrite existing values, including those set by other plugins, unless the current value was explicitly set by the user (Source = USER or Source = LOCKED)." + } + ] + }, + { + "function": "SET_EMPTY", + "type": { + "dataType": "array", + "elements": [ + { + "elementType": "select", + "elementOptions": [{ "multiple": "true", "ordeable": "true" }], + "transformers": [] + } + ] + }, + "default_value": [], + "options": [ + "devFQDN", + "devName" + ], + "localized": ["name", "description"], + "name": [ + { + "language_code": "en_us", + "string": "Set empty columns" + } + ], + "description": [ + { + "language_code": "en_us", + "string": "These columns are only overwritten if they are empty (NULL / empty string) or if their Source is set to NEWDEV" + } + ] } ], "database_column_definitions": [ diff --git a/server/database.py b/server/database.py index d1d5ff08..07678c07 100755 --- a/server/database.py +++ b/server/database.py @@ -208,27 +208,6 @@ class DB: # Init the AppEvent database table AppEvent_obj(self) - # # ------------------------------------------------------------------------------- - # def get_table_as_json(self, sqlQuery): - - # # mylog('debug',[ '[Database] - get_table_as_json - Query: ', sqlQuery]) - # try: - # self.sql.execute(sqlQuery) - # columnNames = list(map(lambda x: x[0], self.sql.description)) - # rows = self.sql.fetchall() - # except sqlite3.Error as e: - # mylog('verbose',[ '[Database] - SQL ERROR: ', e]) - # return json_obj({}, []) # return empty object - - # result = {"data":[]} - # for row in rows: - # tmp = row_to_json(columnNames, row) - # result["data"].append(tmp) - - # # mylog('debug',[ '[Database] - get_table_as_json - returning ', len(rows), " rows with columns: ", columnNames]) - # # mylog('debug',[ '[Database] - get_table_as_json - returning json ', json.dumps(result) ]) - # return json_obj(result, columnNames) - def get_table_as_json(self, sqlQuery, parameters=None): """ Wrapper to use the central get_table_as_json helper. diff --git a/server/db/authoritative_handler.py b/server/db/authoritative_handler.py index 444170ac..d40508c6 100644 --- a/server/db/authoritative_handler.py +++ b/server/db/authoritative_handler.py @@ -96,35 +96,37 @@ def can_overwrite_field(field_name, current_value, current_source, plugin_prefix bool: True if overwrite allowed. """ + empty_values = ("0.0.0.0", "", "null", "(unknown)", "(name not found)", None) + # Rule 1: USER/LOCKED protected if current_source in ("USER", "LOCKED"): return False - # Rule 2: Must provide a non-empty value or same as current - empty_values = ("0.0.0.0", "", "null", "(unknown)", "(name not found)", None) - if not field_value or (isinstance(field_value, str) and not field_value.strip()): - if current_value == field_value: - return True # Allow overwrite if value same + # Rule 2: Must provide a non-empty value, otherwise reject + if (not field_value) or (field_value in empty_values) or ((isinstance(field_value, str) and not field_value.strip())): return False - # Rule 3: SET_ALWAYS + # Rule 3: Allow overwrite if value same to update Source fields + if current_value == field_value: + return True + + # Rule 4: SET_ALWAYS set_always = plugin_settings.get("set_always", []) if field_name in set_always: return True - # Rule 4: SET_EMPTY + # Rule 5: SET_EMPTY set_empty = plugin_settings.get("set_empty", []) - empty_values = ("0.0.0.0", "", "null", "(unknown)", "(name not found)", None) if field_name in set_empty: if current_value in empty_values: return True return False - # Rule 5: Default - overwrite if current value empty + # Rule 6: Default - overwrite if current value empty if current_value in empty_values: return True - # Rule 6: Optional override flag to allow overwrite if value changed (devLastIP) + # Rule 7: Optional override flag to allow overwrite if value changed (devLastIP) if allow_override_if_changed and field_value != current_value: return True diff --git a/server/db/db_upgrade.py b/server/db/db_upgrade.py index 994e7f2f..b79ddf88 100755 --- a/server/db/db_upgrade.py +++ b/server/db/db_upgrade.py @@ -295,6 +295,7 @@ def ensure_CurrentScan(sql) -> bool: scanVlan STRING(250), scanParentMAC STRING(250), scanParentPort STRING(250), + scanFQDN STRING(250), scanType STRING(250) ); """) diff --git a/server/scan/device_handling.py b/server/scan/device_handling.py index 972ac25f..dcb24476 100755 --- a/server/scan/device_handling.py +++ b/server/scan/device_handling.py @@ -102,17 +102,6 @@ FIELD_SPECS = { "priority": ["NSLOOKUP", "AVAHISCAN", "NBTSCAN", "DIGSCAN", "ARPSCAN", "DHCPLSS", "NEWDEV", "N/A"], }, - # ========================================================== - # DEVICE FQDN - # ========================================================== - "devFQDN": { - "scan_col": "scanName", - "source_col": "devNameSource", - "empty_values": ["", "null", "(unknown)", "(name not found)"], - "priority": ["NSLOOKUP", "AVAHISCAN", "NBTSCAN", "DIGSCAN", "ARPSCAN", "DHCPLSS", "NEWDEV", "N/A"], - "allow_override_if_changed": True, - }, - # ========================================================== # IP ADDRESS (last seen) # ========================================================== @@ -297,8 +286,6 @@ def update_devices_data_from_scan(db): current_source = row_dict.get(f"{field}Source") or "" new_value = row_dict.get(scan_col) - mylog("debug", f"[Update Devices] - current_value: {current_value} new_value: {new_value} -> {field}") - if can_overwrite_field( field_name=field, current_value=current_value, @@ -326,6 +313,7 @@ def update_devices_data_from_scan(db): WHERE devMac = ? """ + mylog("debug", f"[Update Devices] - ({source_prefix}) current_value: {current_value} new_value: {new_value} -> {field}") mylog("debug", f"[Update Devices] - ({source_prefix}) {spec['scan_col']} -> {field}") mylog("debug", f"[Update Devices] sql_tmp: {sql_tmp}, sql_val: {sql_val}") sql.execute(sql_tmp, sql_val) @@ -978,34 +966,38 @@ def update_devices_names(pm): notFound = 0 for device in devices: - newName = nameNotFound - newFQDN = "" + resolved_successfully = False # Attempt each resolution strategy in order for resolve_fn, label in strategies: resolved = resolve_fn(device["devMac"], device["devLastIP"]) - # Only use name if resolving both name and FQDN - newName = resolved.cleaned if resolve_both_name_and_fqdn else None - newFQDN = resolved.raw + # Extract values + current_raw = resolved.raw if resolved.raw else "" + current_cleaned = resolved.cleaned if resolved.cleaned else "" + + # Validation: Ensure we actually got a real FQDN/Name + if (current_raw not in [nameNotFound, "", "localhost."] and " communications error to " not in current_raw): - # If a valid result is found, record it and stop further attempts - if ( - newFQDN not in [nameNotFound, "", "localhost."] and " communications error to " not in newFQDN - ): foundStats[label] += 1 + resolved_successfully = True if resolve_both_name_and_fqdn: - recordsToUpdate.append([newName, label, newFQDN, label, device["devMac"]]) + # Logic: If cleaned name is missing, fallback to raw, + # but if both are missing, this strategy failed. + final_name = current_cleaned if current_cleaned else current_raw + recordsToUpdate.append([final_name, label, current_raw, label, device["devMac"]]) else: - recordsToUpdate.append([newFQDN, label, device["devMac"]]) - break + recordsToUpdate.append([current_raw, label, device["devMac"]]) - # If no name was resolved, queue device for "(name not found)" update - if resolve_both_name_and_fqdn and newName == nameNotFound: + break # Stop trying other strategies for this device + + # If after all strategies we found nothing + if not resolved_successfully: notFound += 1 - if device["devName"] != nameNotFound: - recordsNotFound.append([nameNotFound, device["devMac"]]) + if resolve_both_name_and_fqdn: + if device["devName"] != nameNotFound: + recordsNotFound.append([nameNotFound, device["devMac"]]) return recordsToUpdate, recordsNotFound, foundStats, notFound diff --git a/server/scan/session_events.py b/server/scan/session_events.py index 3f9f1022..2a051ed8 100755 --- a/server/scan/session_events.py +++ b/server/scan/session_events.py @@ -103,7 +103,7 @@ def process_scan(db): # Clear current scan as processed # 🐛 CurrentScan DEBUG: comment out below when debugging to keep the CurrentScan table after restarts/scan finishes - db.sql.execute("DELETE FROM CurrentScan") + # db.sql.execute("DELETE FROM CurrentScan") # re-broadcast unread notifiation count to update FE update_unread_notifications_count()