diff --git a/server/app_state.py b/server/app_state.py index bc867134..b85533d9 100755 --- a/server/app_state.py +++ b/server/app_state.py @@ -107,8 +107,12 @@ class app_state_class: if pluginsStates is not None: for plugin, state in pluginsStates.items(): if plugin in self.pluginsStates: - # Only update existing keys - self.pluginsStates[plugin].update(state) + # Only update existing keys if both are dicts + if isinstance(self.pluginsStates[plugin], dict) and isinstance(state, dict): + self.pluginsStates[plugin].update(state) + else: + # Replace if types don't match + self.pluginsStates[plugin] = state else: # Optionally ignore or add new plugin entries # To ignore new plugins, comment out the next line diff --git a/server/plugin.py b/server/plugin.py index 29cbbad2..616e6727 100755 --- a/server/plugin.py +++ b/server/plugin.py @@ -212,7 +212,8 @@ class plugin_manager: "lastChanged": str, "totalObjects": int, "newObjects": int, - "changedObjects": int + "changedObjects": int, + "stateUpdated": str } """ sql = self.db.sql @@ -222,12 +223,13 @@ class plugin_manager: sql.execute(""" SELECT MAX(DateTimeChanged) AS last_changed, COUNT(*) AS total_objects, - SUM(CASE WHEN DateTimeCreated = DateTimeChanged THEN 1 ELSE 0 END) AS new_objects + SUM(CASE WHEN DateTimeCreated = DateTimeChanged THEN 1 ELSE 0 END) AS new_objects, + CURRENT_TIMESTAMP AS state_updated FROM Plugins_Objects WHERE Plugin = ? """, (plugin_name,)) row = sql.fetchone() - last_changed, total_objects, new_objects = row if row else ("", 0, 0) + last_changed, total_objects, new_objects, state_updated = row if row else ("", 0, 0) new_objects = new_objects or 0 # ensure it's int changed_objects = total_objects - new_objects @@ -235,7 +237,8 @@ class plugin_manager: "lastChanged": last_changed or "", "totalObjects": total_objects or 0, "newObjects": new_objects or 0, - "changedObjects": changed_objects or 0 + "changedObjects": changed_objects or 0, + "stateUpdated": state_updated or "" } # Save in memory @@ -246,18 +249,20 @@ class plugin_manager: SELECT Plugin, MAX(DateTimeChanged) AS last_changed, COUNT(*) AS total_objects, - SUM(CASE WHEN DateTimeCreated = DateTimeChanged THEN 1 ELSE 0 END) AS new_objects + SUM(CASE WHEN DateTimeCreated = DateTimeChanged THEN 1 ELSE 0 END) AS new_objects, + CURRENT_TIMESTAMP AS state_updated FROM Plugins_Objects GROUP BY Plugin """) - for plugin, last_changed, total_objects, new_objects in sql.fetchall(): + for plugin, last_changed, total_objects, new_objects, state_updated in sql.fetchall(): new_objects = new_objects or 0 # ensure it's int changed_objects = total_objects - new_objects plugin_states[plugin] = { "lastChanged": last_changed or "", "totalObjects": total_objects or 0, "newObjects": new_objects or 0, - "changedObjects": changed_objects or 0 + "changedObjects": changed_objects or 0, + "stateUpdated": state_updated or "" } # Save in memory diff --git a/server/scan/device_handling.py b/server/scan/device_handling.py index b0169473..f908661c 100755 --- a/server/scan/device_handling.py +++ b/server/scan/device_handling.py @@ -3,19 +3,23 @@ import subprocess import conf import os import re +from dateutil import parser # Register NetAlertX directories INSTALL_PATH="/app" sys.path.extend([f"{INSTALL_PATH}/server"]) from helper import timeNowTZ, get_setting_value, check_IP_format -from logger import mylog +from logger import mylog, Logger from const import vendorsPath, vendorsPathNewest, sql_generateGuid from models.device_instance import DeviceInstance from scan.name_resolution import NameResolver from scan.device_heuristics import guess_icon, guess_type from db.db_helper import sanitize_SQL_input, list_to_where +# Make sure log level is initialized correctly +Logger(get_setting_value('LOG_LEVEL')) + #------------------------------------------------------------------------------- # Removing devices from the CurrentScan DB table which the user chose to ignore by MAC or IP def exclude_ignored_devices(db): @@ -521,25 +525,25 @@ def update_devices_names(pm): resolver = NameResolver(pm.db) device_handler = DeviceInstance(pm.db) - # --- Short-circuit if no plugin that resolves names changed --- + # --- Short-circuit if no name-resolution plugin has changed --- name_plugins = ["DIGSCAN", "NSLOOKUP", "NBTSCAN", "AVAHISCAN"] - - # Get last check timestamp from plugin manager - last_checked = pm.name_plugins_checked - # Determine the latest 'lastChanged' timestamp among name plugins - latest_change = max( - [pm.plugin_states.get(p, {}).get("lastChanged") for p in name_plugins if pm.plugin_states.get(p)], - default=None - ) + # Retrieve last time name resolution was checked (string or datetime) + last_checked_str = pm.name_plugins_checked + last_checked_dt = parser.parse(last_checked_str) if isinstance(last_checked_str, str) else last_checked_str - # Convert to comparable datetime if needed - from dateutil import parser - latest_change_dt = parser.parse(latest_change) if latest_change else None + # Find the most recent plugin update time among name-related plugins + state_times = [ + pm.plugin_states.get(p, {}).get("stateUpdated") + for p in name_plugins + if pm.plugin_states.get(p) + ] + latest_state_str = max(state_times, default=None) + latest_state_dt = parser.parse(latest_state_str) if latest_state_str else None - # Skip if nothing changed since last check - if last_checked and latest_change_dt and latest_change_dt <= last_checked: - mylog('debug', '[Update Device Name] No relevant plugin changes since last check, skipping.') + # Skip if no plugin state changed since last check + if last_checked_dt and latest_state_dt and latest_state_dt <= last_checked_dt: + mylog('debug', '[Update Device Name] No relevant name plugin changes since last check — skipping update.') return nameNotFound = "(name not found)" @@ -639,7 +643,10 @@ def update_devices_names(pm): # --- Step 3: Log last checked time --- # After resolving names, update last checked - pm.name_plugins_checked = timeNowTZ() + sql = pm.db.sql + sql.execute("SELECT CURRENT_TIMESTAMP") + row = sql.fetchone() + pm.name_plugins_checked = row[0] if row else None #------------------------------------------------------------------------------- # Updates devPresentLastScan for parent devices based on the presence of their NICs