mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-04-05 01:31:49 -07:00
feat: authoritative plugin fields
Signed-off-by: jokob-sk <jokob.sk@gmail.com>
This commit is contained in:
@@ -41,6 +41,7 @@ CREATE TABLE Devices (
|
|||||||
devIsArchived BOOLEAN NOT NULL DEFAULT (0) CHECK (devIsArchived IN (0, 1)),
|
devIsArchived BOOLEAN NOT NULL DEFAULT (0) CHECK (devIsArchived IN (0, 1)),
|
||||||
devParentMAC TEXT,
|
devParentMAC TEXT,
|
||||||
devParentPort INTEGER,
|
devParentPort INTEGER,
|
||||||
|
devParentRelType TEXT,
|
||||||
devIcon TEXT,
|
devIcon TEXT,
|
||||||
devGUID TEXT,
|
devGUID TEXT,
|
||||||
devSite TEXT,
|
devSite TEXT,
|
||||||
@@ -49,11 +50,11 @@ CREATE TABLE Devices (
|
|||||||
devSourcePlugin TEXT,
|
devSourcePlugin TEXT,
|
||||||
devMacSource TEXT,
|
devMacSource TEXT,
|
||||||
devNameSource TEXT,
|
devNameSource TEXT,
|
||||||
devFqdnSource TEXT,
|
devFQDNSource TEXT,
|
||||||
devLastIpSource TEXT,
|
devLastIPSource TEXT,
|
||||||
devVendorSource TEXT,
|
devVendorSource TEXT,
|
||||||
devSsidSource TEXT,
|
devSSIDSource TEXT,
|
||||||
devParentMacSource TEXT,
|
devParentMACSource TEXT,
|
||||||
devParentPortSource TEXT,
|
devParentPortSource TEXT,
|
||||||
devParentRelTypeSource TEXT,
|
devParentRelTypeSource TEXT,
|
||||||
devVlanSource TEXT,
|
devVlanSource TEXT,
|
||||||
|
|||||||
@@ -271,11 +271,16 @@ function getDeviceData() {
|
|||||||
</span>`;
|
</span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// timestamps
|
||||||
|
if (setting.setKey == "NEWDEV_devFirstConnection" || setting.setKey == "NEWDEV_devLastConnection") {
|
||||||
|
fieldData = localizeTimestamp(fieldData)
|
||||||
|
}
|
||||||
|
|
||||||
// Add lock/unlock button for tracked fields (not for new devices)
|
// Add lock/unlock button for tracked fields (not for new devices)
|
||||||
const fieldName = setting.setKey.replace('NEWDEV_', '');
|
const fieldName = setting.setKey.replace('NEWDEV_', '');
|
||||||
if (trackedFields[fieldName] && mac != "new") {
|
if (trackedFields[fieldName] && mac != "new") {
|
||||||
const sourceField = fieldName + "Source";
|
const sourceField = fieldName + "Source";
|
||||||
const currentSource = deviceData[sourceField] || "UNKNOWN";
|
const currentSource = deviceData[sourceField] || "N/A";
|
||||||
const isLocked = currentSource === "LOCKED";
|
const isLocked = currentSource === "LOCKED";
|
||||||
const lockIcon = isLocked ? "fa-lock" : "fa-lock-open";
|
const lockIcon = isLocked ? "fa-lock" : "fa-lock-open";
|
||||||
const lockTitle = isLocked ? getString("FieldLock_Unlock_Tooltip") : getString("FieldLock_Lock_Tooltip");
|
const lockTitle = isLocked ? getString("FieldLock_Unlock_Tooltip") : getString("FieldLock_Lock_Tooltip");
|
||||||
@@ -292,7 +297,7 @@ function getDeviceData() {
|
|||||||
const fieldName2 = setting.setKey.replace('NEWDEV_', '');
|
const fieldName2 = setting.setKey.replace('NEWDEV_', '');
|
||||||
if (trackedFields[fieldName2] && mac != "new") {
|
if (trackedFields[fieldName2] && mac != "new") {
|
||||||
const sourceField = fieldName2 + "Source";
|
const sourceField = fieldName2 + "Source";
|
||||||
const currentSource = deviceData[sourceField] || "UNKNOWN";
|
const currentSource = deviceData[sourceField] || "N/A";
|
||||||
const sourceTitle = getString("FieldLock_Source_Label") + currentSource;
|
const sourceTitle = getString("FieldLock_Source_Label") + currentSource;
|
||||||
const sourceColor = currentSource === "USER" ? "text-warning" : (currentSource === "LOCKED" ? "text-danger" : "text-muted");
|
const sourceColor = currentSource === "USER" ? "text-warning" : (currentSource === "LOCKED" ? "text-danger" : "text-muted");
|
||||||
inlineControl += `<span class="input-group-addon pointer ${sourceColor}" title="${sourceTitle}">
|
inlineControl += `<span class="input-group-addon pointer ${sourceColor}" title="${sourceTitle}">
|
||||||
@@ -406,7 +411,7 @@ function setDeviceData(direction = '', refreshCallback = '') {
|
|||||||
|
|
||||||
mac = $('#NEWDEV_devMac').val();
|
mac = $('#NEWDEV_devMac').val();
|
||||||
|
|
||||||
// Build payload for new endpoint
|
// Build payload
|
||||||
const payload = {
|
const payload = {
|
||||||
devName: $('#NEWDEV_devName').val().replace(/'/g, "’"),
|
devName: $('#NEWDEV_devName').val().replace(/'/g, "’"),
|
||||||
devOwner: $('#NEWDEV_devOwner').val().replace(/'/g, "’"),
|
devOwner: $('#NEWDEV_devOwner').val().replace(/'/g, "’"),
|
||||||
@@ -432,6 +437,7 @@ function setDeviceData(direction = '', refreshCallback = '') {
|
|||||||
devAlertEvents: ($('#NEWDEV_devAlertEvents')[0].checked * 1),
|
devAlertEvents: ($('#NEWDEV_devAlertEvents')[0].checked * 1),
|
||||||
devAlertDown: ($('#NEWDEV_devAlertDown')[0].checked * 1),
|
devAlertDown: ($('#NEWDEV_devAlertDown')[0].checked * 1),
|
||||||
devSkipRepeated: $('#NEWDEV_devSkipRepeated').val().split(' ')[0],
|
devSkipRepeated: $('#NEWDEV_devSkipRepeated').val().split(' ')[0],
|
||||||
|
devForceStatus: $('#NEWDEV_devForceStatus').val().replace(/'/g, ""),
|
||||||
|
|
||||||
devReqNicsOnline: ($('#NEWDEV_devReqNicsOnline')[0].checked * 1),
|
devReqNicsOnline: ($('#NEWDEV_devReqNicsOnline')[0].checked * 1),
|
||||||
devIsNew: ($('#NEWDEV_devIsNew')[0].checked * 1),
|
devIsNew: ($('#NEWDEV_devIsNew')[0].checked * 1),
|
||||||
@@ -561,7 +567,7 @@ function toggleFieldLock(mac, fieldName) {
|
|||||||
|
|
||||||
// Get current source value
|
// Get current source value
|
||||||
const sourceField = fieldName + "Source";
|
const sourceField = fieldName + "Source";
|
||||||
const currentSource = deviceData[sourceField] || "UNKNOWN";
|
const currentSource = deviceData[sourceField] || "N/A";
|
||||||
const shouldLock = currentSource !== "LOCKED";
|
const shouldLock = currentSource !== "LOCKED";
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
@@ -600,7 +606,7 @@ function toggleFieldLock(mac, fieldName) {
|
|||||||
// Update source indicator
|
// Update source indicator
|
||||||
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" : "N/A";
|
||||||
const sourceClass = shouldLock ? "input-group-addon text-danger" : "input-group-addon pointer 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);
|
||||||
|
|||||||
@@ -639,7 +639,10 @@ function ImportPastedCSV()
|
|||||||
data: JSON.stringify({ content: csvBase64 }),
|
data: JSON.stringify({ content: csvBase64 }),
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
showMessage(response.success ? (response.message || "Devices imported successfully") : (response.error || "Unknown error"));
|
|
||||||
|
console.log(response);
|
||||||
|
|
||||||
|
showMessage(response.success ? (response.message || response.inserted + " Devices imported successfully") : (response.error || "Unknown error"));
|
||||||
write_notification(`[Maintenance] Devices imported from pasted content`, 'info');
|
write_notification(`[Maintenance] Devices imported from pasted content`, 'info');
|
||||||
},
|
},
|
||||||
error: function(xhr, status, error) {
|
error: function(xhr, status, error) {
|
||||||
|
|||||||
@@ -163,16 +163,16 @@ class DB:
|
|||||||
raise RuntimeError("ensure_column(devMacSource) failed")
|
raise RuntimeError("ensure_column(devMacSource) failed")
|
||||||
if not ensure_column(self.sql, "Devices", "devNameSource", "TEXT"):
|
if not ensure_column(self.sql, "Devices", "devNameSource", "TEXT"):
|
||||||
raise RuntimeError("ensure_column(devNameSource) failed")
|
raise RuntimeError("ensure_column(devNameSource) failed")
|
||||||
if not ensure_column(self.sql, "Devices", "devFqdnSource", "TEXT"):
|
if not ensure_column(self.sql, "Devices", "devFQDNSource", "TEXT"):
|
||||||
raise RuntimeError("ensure_column(devFqdnSource) failed")
|
raise RuntimeError("ensure_column(devFQDNSource) failed")
|
||||||
if not ensure_column(self.sql, "Devices", "devLastIpSource", "TEXT"):
|
if not ensure_column(self.sql, "Devices", "devLastIPSource", "TEXT"):
|
||||||
raise RuntimeError("ensure_column(devLastIpSource) failed")
|
raise RuntimeError("ensure_column(devLastIPSource) failed")
|
||||||
if not ensure_column(self.sql, "Devices", "devVendorSource", "TEXT"):
|
if not ensure_column(self.sql, "Devices", "devVendorSource", "TEXT"):
|
||||||
raise RuntimeError("ensure_column(devVendorSource) failed")
|
raise RuntimeError("ensure_column(devVendorSource) failed")
|
||||||
if not ensure_column(self.sql, "Devices", "devSsidSource", "TEXT"):
|
if not ensure_column(self.sql, "Devices", "devSSIDSource", "TEXT"):
|
||||||
raise RuntimeError("ensure_column(devSsidSource) failed")
|
raise RuntimeError("ensure_column(devSSIDSource) failed")
|
||||||
if not ensure_column(self.sql, "Devices", "devParentMacSource", "TEXT"):
|
if not ensure_column(self.sql, "Devices", "devParentMACSource", "TEXT"):
|
||||||
raise RuntimeError("ensure_column(devParentMacSource) failed")
|
raise RuntimeError("ensure_column(devParentMACSource) failed")
|
||||||
if not ensure_column(self.sql, "Devices", "devParentPortSource", "TEXT"):
|
if not ensure_column(self.sql, "Devices", "devParentPortSource", "TEXT"):
|
||||||
raise RuntimeError("ensure_column(devParentPortSource) failed")
|
raise RuntimeError("ensure_column(devParentPortSource) failed")
|
||||||
if not ensure_column(self.sql, "Devices", "devParentRelTypeSource", "TEXT"):
|
if not ensure_column(self.sql, "Devices", "devParentRelTypeSource", "TEXT"):
|
||||||
|
|||||||
@@ -73,49 +73,54 @@ def get_plugin_authoritative_settings(plugin_prefix):
|
|||||||
return {"set_always": [], "set_empty": []}
|
return {"set_always": [], "set_empty": []}
|
||||||
|
|
||||||
|
|
||||||
def can_overwrite_field(field_name, current_source, plugin_prefix, plugin_settings, field_value):
|
def can_overwrite_field(field_name, current_value, current_source, plugin_prefix, plugin_settings, field_value):
|
||||||
"""
|
"""
|
||||||
Determine if a plugin can overwrite a field.
|
Determine if a plugin can overwrite a field.
|
||||||
|
|
||||||
Rules:
|
Rules:
|
||||||
- If current_source is USER or LOCKED, cannot overwrite.
|
- USER/LOCKED cannot overwrite.
|
||||||
- If field_value is empty/None, cannot overwrite.
|
- SET_ALWAYS can overwrite everything if new value not empty.
|
||||||
- If field is in SET_ALWAYS, can overwrite.
|
- SET_EMPTY can overwrite if current value empty.
|
||||||
- If field is in SET_EMPTY AND current value is empty, can overwrite.
|
- Otherwise, overwrite only empty fields.
|
||||||
- If neither SET_ALWAYS nor SET_EMPTY apply, can overwrite empty fields only.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
field_name: The field being updated (e.g., "devName").
|
field_name: The field being updated (e.g., "devName").
|
||||||
current_source: The current source value (e.g., "USER", "LOCKED", "ARPSCAN", "NEWDEV", "").
|
current_value: Current value in Devices.
|
||||||
plugin_prefix: The unique prefix of the overwriting plugin.
|
current_source: Current source in Devices (USER, LOCKED, etc.).
|
||||||
plugin_settings: dict with "set_always" and "set_empty" lists.
|
plugin_prefix: Plugin prefix.
|
||||||
field_value: The new value the plugin wants to write.
|
plugin_settings: Dict with set_always and set_empty lists.
|
||||||
|
field_value: The new value from scan.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True if the overwrite is allowed, False otherwise.
|
bool: True if overwrite allowed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Rule 1: USER and LOCKED are protected
|
# Rule 1: USER/LOCKED protected
|
||||||
if current_source in ("USER", "LOCKED"):
|
if current_source in ("USER", "LOCKED"):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Rule 2: Plugin must provide a non-empty value
|
# 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 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
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Rule 3: SET_ALWAYS takes precedence
|
# Rule 3: SET_ALWAYS
|
||||||
set_always = plugin_settings.get("set_always", [])
|
set_always = plugin_settings.get("set_always", [])
|
||||||
if field_name in set_always:
|
if field_name in set_always:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Rule 4: SET_EMPTY allows overwriting only if field is empty
|
# Rule 4: SET_EMPTY
|
||||||
set_empty = plugin_settings.get("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 field_name in set_empty:
|
||||||
# Check if field is "empty" (no current source or NEWDEV)
|
if current_value in empty_values:
|
||||||
return not current_source or current_source == "NEWDEV"
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
# Rule 5: Default behavior - overwrite if field is empty/NEWDEV
|
# Rule 5: Default - overwrite if current value empty
|
||||||
return not current_source or current_source == "NEWDEV"
|
return current_value in empty_values
|
||||||
|
|
||||||
|
|
||||||
def get_overwrite_sql_clause(field_name, source_column, plugin_settings):
|
def get_overwrite_sql_clause(field_name, source_column, plugin_settings):
|
||||||
@@ -136,6 +141,8 @@ def get_overwrite_sql_clause(field_name, source_column, plugin_settings):
|
|||||||
set_always = plugin_settings.get("set_always", [])
|
set_always = plugin_settings.get("set_always", [])
|
||||||
set_empty = plugin_settings.get("set_empty", [])
|
set_empty = plugin_settings.get("set_empty", [])
|
||||||
|
|
||||||
|
mylog("debug", [f"[get_overwrite_sql_clause] DEBUG: field_name:{field_name}, source_column:{source_column}, set_always:{set_always}, set_empty:{set_empty}"])
|
||||||
|
|
||||||
if field_name in set_always:
|
if field_name in set_always:
|
||||||
return f"COALESCE({source_column}, '') NOT IN ('USER', 'LOCKED')"
|
return f"COALESCE({source_column}, '') NOT IN ('USER', 'LOCKED')"
|
||||||
|
|
||||||
|
|||||||
@@ -171,6 +171,27 @@ def ensure_views(sql) -> bool:
|
|||||||
EVE1.eve_PairEventRowID IS NULL;
|
EVE1.eve_PairEventRowID IS NULL;
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
sql.execute(""" DROP VIEW IF EXISTS LatestDeviceScan;""")
|
||||||
|
sql.execute(""" CREATE VIEW LatestDeviceScan AS
|
||||||
|
WITH RankedScans AS (
|
||||||
|
SELECT
|
||||||
|
c.*,
|
||||||
|
ROW_NUMBER() OVER (
|
||||||
|
PARTITION BY c.cur_MAC, c.cur_ScanMethod
|
||||||
|
ORDER BY c.cur_DateTime DESC
|
||||||
|
) AS rn
|
||||||
|
FROM CurrentScan c
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
d.*, -- all Device fields
|
||||||
|
r.* -- all CurrentScan fields (cur_*)
|
||||||
|
FROM Devices d
|
||||||
|
LEFT JOIN RankedScans r
|
||||||
|
ON d.devMac = r.cur_MAC
|
||||||
|
WHERE r.rn = 1;
|
||||||
|
|
||||||
|
""")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -418,7 +418,7 @@ class DeviceInstance:
|
|||||||
"devGUID": "",
|
"devGUID": "",
|
||||||
"devSite": "",
|
"devSite": "",
|
||||||
"devSSID": "",
|
"devSSID": "",
|
||||||
"devSyncHubNode": "",
|
"devSyncHubNode": str(get_setting_value("SYNC_node_name")),
|
||||||
"devSourcePlugin": "",
|
"devSourcePlugin": "",
|
||||||
"devCustomProps": "",
|
"devCustomProps": "",
|
||||||
"devStatus": "Unknown",
|
"devStatus": "Unknown",
|
||||||
@@ -428,6 +428,7 @@ class DeviceInstance:
|
|||||||
"devDownAlerts": 0,
|
"devDownAlerts": 0,
|
||||||
"devPresenceHours": 0,
|
"devPresenceHours": 0,
|
||||||
"devFQDN": "",
|
"devFQDN": "",
|
||||||
|
"devForceStatus" : "dont_force"
|
||||||
}
|
}
|
||||||
return device_data
|
return device_data
|
||||||
|
|
||||||
@@ -534,6 +535,7 @@ class DeviceInstance:
|
|||||||
"devIsNew",
|
"devIsNew",
|
||||||
"devIsArchived",
|
"devIsArchived",
|
||||||
"devCustomProps",
|
"devCustomProps",
|
||||||
|
"devForceStatus"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Only mark USER for tracked fields that this method actually updates.
|
# Only mark USER for tracked fields that this method actually updates.
|
||||||
@@ -583,8 +585,8 @@ class DeviceInstance:
|
|||||||
devParentRelType, devReqNicsOnline, devSkipRepeated,
|
devParentRelType, devReqNicsOnline, devSkipRepeated,
|
||||||
devIsNew, devIsArchived, devLastConnection,
|
devIsNew, devIsArchived, devLastConnection,
|
||||||
devFirstConnection, devLastIP, devGUID, devCustomProps,
|
devFirstConnection, devLastIP, devGUID, devCustomProps,
|
||||||
devSourcePlugin
|
devSourcePlugin, devForceStatus
|
||||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
values = (
|
values = (
|
||||||
@@ -617,6 +619,7 @@ class DeviceInstance:
|
|||||||
data.get("devGUID") or "",
|
data.get("devGUID") or "",
|
||||||
data.get("devCustomProps") or "",
|
data.get("devCustomProps") or "",
|
||||||
data.get("devSourcePlugin") or "DUMMY",
|
data.get("devSourcePlugin") or "DUMMY",
|
||||||
|
data.get("devForceStatus") or "dont_force",
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@@ -627,7 +630,7 @@ class DeviceInstance:
|
|||||||
devParentMAC=?, devParentPort=?, devSSID=?, devSite=?,
|
devParentMAC=?, devParentPort=?, devSSID=?, devSite=?,
|
||||||
devStaticIP=?, devScan=?, devAlertEvents=?, devAlertDown=?,
|
devStaticIP=?, devScan=?, devAlertEvents=?, devAlertDown=?,
|
||||||
devParentRelType=?, devReqNicsOnline=?, devSkipRepeated=?,
|
devParentRelType=?, devReqNicsOnline=?, devSkipRepeated=?,
|
||||||
devIsNew=?, devIsArchived=?, devCustomProps=?
|
devIsNew=?, devIsArchived=?, devCustomProps=?, devForceStatus=?
|
||||||
WHERE devMac=?
|
WHERE devMac=?
|
||||||
"""
|
"""
|
||||||
values = (
|
values = (
|
||||||
@@ -654,6 +657,7 @@ class DeviceInstance:
|
|||||||
data.get("devIsNew") or 0,
|
data.get("devIsNew") or 0,
|
||||||
data.get("devIsArchived") or 0,
|
data.get("devIsArchived") or 0,
|
||||||
data.get("devCustomProps") or "",
|
data.get("devCustomProps") or "",
|
||||||
|
data.get("devForceStatus") or "dont_force",
|
||||||
normalized_mac,
|
normalized_mac,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import ipaddress
|
||||||
from helper import get_setting_value, check_IP_format
|
from helper import get_setting_value, check_IP_format
|
||||||
from utils.datetime_utils import timeNowDB, normalizeTimeStamp
|
from utils.datetime_utils import timeNowDB, normalizeTimeStamp
|
||||||
from logger import mylog, Logger
|
from logger import mylog, Logger
|
||||||
@@ -11,14 +12,43 @@ 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 (
|
from db.authoritative_handler import (
|
||||||
get_overwrite_sql_clause,
|
get_overwrite_sql_clause,
|
||||||
|
can_overwrite_field,
|
||||||
get_plugin_authoritative_settings,
|
get_plugin_authoritative_settings,
|
||||||
get_source_for_field_update_with_value,
|
get_source_for_field_update_with_value,
|
||||||
|
FIELD_SOURCE_MAP
|
||||||
)
|
)
|
||||||
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
|
||||||
Logger(get_setting_value("LOG_LEVEL"))
|
Logger(get_setting_value("LOG_LEVEL"))
|
||||||
|
|
||||||
|
_device_columns_cache = None
|
||||||
|
|
||||||
|
|
||||||
|
def get_device_columns(sql, force_reload=False):
|
||||||
|
"""
|
||||||
|
Return a set of column names in the Devices table.
|
||||||
|
|
||||||
|
Cached after first call unless force_reload=True.
|
||||||
|
"""
|
||||||
|
global _device_columns_cache
|
||||||
|
if _device_columns_cache is None or force_reload:
|
||||||
|
try:
|
||||||
|
_device_columns_cache = {row["name"] for row in sql.execute("PRAGMA table_info(Devices)").fetchall()}
|
||||||
|
except Exception:
|
||||||
|
_device_columns_cache = set()
|
||||||
|
return _device_columns_cache
|
||||||
|
|
||||||
|
|
||||||
|
def has_column(sql, column_name):
|
||||||
|
"""
|
||||||
|
Check if a column exists in Devices table.
|
||||||
|
|
||||||
|
Uses cached columns.
|
||||||
|
"""
|
||||||
|
device_columns = get_device_columns(sql)
|
||||||
|
return column_name in device_columns
|
||||||
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
# Removing devices from the CurrentScan DB table which the user chose to ignore by MAC or IP
|
# Removing devices from the CurrentScan DB table which the user chose to ignore by MAC or IP
|
||||||
@@ -57,574 +87,287 @@ def exclude_ignored_devices(db):
|
|||||||
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
def update_devices_data_from_scan(db):
|
FIELD_SPECS = {
|
||||||
sql = db.sql # TO-DO
|
|
||||||
|
# ==========================================================
|
||||||
|
# DEVICE NAME
|
||||||
|
# ==========================================================
|
||||||
|
"devName": {
|
||||||
|
"scan_col": "cur_Name",
|
||||||
|
"source_col": "devNameSource",
|
||||||
|
"empty_values": ["", "null", "(unknown)", "(name not found)"],
|
||||||
|
"default_value": "(unknown)",
|
||||||
|
"priority": ["NSLOOKUP", "AVAHISCAN", "NBTSCAN", "DIGSCAN", "ARPSCAN", "DHCPLSS", "NEWDEV", "N/A"],
|
||||||
|
},
|
||||||
|
|
||||||
|
# ==========================================================
|
||||||
|
# DEVICE FQDN
|
||||||
|
# ==========================================================
|
||||||
|
"devFQDN": {
|
||||||
|
"scan_col": "cur_Name",
|
||||||
|
"source_col": "devNameSource",
|
||||||
|
"empty_values": ["", "null", "(unknown)", "(name not found)"],
|
||||||
|
"priority": ["NSLOOKUP", "AVAHISCAN", "NBTSCAN", "DIGSCAN", "ARPSCAN", "DHCPLSS", "NEWDEV", "N/A"],
|
||||||
|
},
|
||||||
|
|
||||||
|
# ==========================================================
|
||||||
|
# IP ADDRESS (last seen)
|
||||||
|
# ==========================================================
|
||||||
|
"devLastIP": {
|
||||||
|
"scan_col": "cur_IP",
|
||||||
|
"source_col": "devLastIpSource",
|
||||||
|
"empty_values": ["", "null", "(unknown)", "(Unknown)"],
|
||||||
|
"priority": ["ARPSCAN", "NEWDEV", "N/A"],
|
||||||
|
"default_value": "0.0.0.0",
|
||||||
|
},
|
||||||
|
|
||||||
|
# ==========================================================
|
||||||
|
# VENDOR
|
||||||
|
# ==========================================================
|
||||||
|
"devVendor": {
|
||||||
|
"scan_col": "cur_Vendor",
|
||||||
|
"source_col": "devVendorSource",
|
||||||
|
"empty_values": ["", "null", "(unknown)", "(Unknown)"],
|
||||||
|
"priority": ["VNDRPDT", "ARPSCAN", "NEWDEV", "N/A"],
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
# ==========================================================
|
||||||
|
# SYNC HUB NODE NAME
|
||||||
|
# ==========================================================
|
||||||
|
"devSyncHubNode": {
|
||||||
|
"scan_col": "cur_SyncHubNodeName",
|
||||||
|
"source_col": None,
|
||||||
|
"empty_values": ["", "null"],
|
||||||
|
"priority": None,
|
||||||
|
},
|
||||||
|
|
||||||
|
# ==========================================================
|
||||||
|
# Network Site
|
||||||
|
# ==========================================================
|
||||||
|
"devSite": {
|
||||||
|
"scan_col": "cur_NetworkSite",
|
||||||
|
"source_col": None,
|
||||||
|
"empty_values": ["", "null"],
|
||||||
|
"priority": None,
|
||||||
|
},
|
||||||
|
|
||||||
|
# ==========================================================
|
||||||
|
# VLAN
|
||||||
|
# ==========================================================
|
||||||
|
"devVlan": {
|
||||||
|
"scan_col": "cur_devVlan",
|
||||||
|
"source_col": "devVlanSource",
|
||||||
|
"empty_values": ["", "null"],
|
||||||
|
"priority": None,
|
||||||
|
},
|
||||||
|
|
||||||
|
# ==========================================================
|
||||||
|
# devType
|
||||||
|
# ==========================================================
|
||||||
|
"devType": {
|
||||||
|
"scan_col": "cur_Type",
|
||||||
|
"source_col": None,
|
||||||
|
"empty_values": ["", "null"],
|
||||||
|
"priority": None,
|
||||||
|
},
|
||||||
|
|
||||||
|
# ==========================================================
|
||||||
|
# TOPOLOGY (PARENT NODE)
|
||||||
|
# ==========================================================
|
||||||
|
"devParentMAC": {
|
||||||
|
"scan_col": "cur_NetworkNodeMAC",
|
||||||
|
"source_col": "devParentMacSource",
|
||||||
|
"empty_values": ["", "null"],
|
||||||
|
"priority": ["SNMPDSC", "UNIFIAPI", "UNFIMP", "NEWDEV", "N/A"],
|
||||||
|
},
|
||||||
|
|
||||||
|
"devParentPort": {
|
||||||
|
"scan_col": "cur_PORT",
|
||||||
|
"source_col": None,
|
||||||
|
"empty_values": ["", "null"],
|
||||||
|
"priority": ["SNMPDSC", "UNIFIAPI", "UNFIMP", "NEWDEV", "N/A"],
|
||||||
|
},
|
||||||
|
|
||||||
|
# ==========================================================
|
||||||
|
# WIFI SSID
|
||||||
|
# ==========================================================
|
||||||
|
"devSSID": {
|
||||||
|
"scan_col": "cur_SSID",
|
||||||
|
"source_col": None,
|
||||||
|
"empty_values": ["", "null"],
|
||||||
|
"priority": ["SNMPDSC", "UNIFIAPI", "UNFIMP", "NEWDEV", "N/A"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def update_presence_from_CurrentScan(db):
|
||||||
|
"""
|
||||||
|
Update devPresentLastScan based on whether the device has entries in CurrentScan.
|
||||||
|
"""
|
||||||
|
sql = db.sql
|
||||||
|
mylog("debug", "[Update Devices] - Updating devPresentLastScan")
|
||||||
|
|
||||||
|
# Mark present if exists in CurrentScan
|
||||||
|
sql.execute("""
|
||||||
|
UPDATE Devices
|
||||||
|
SET devPresentLastScan = 1
|
||||||
|
WHERE EXISTS (
|
||||||
|
SELECT 1 FROM CurrentScan
|
||||||
|
WHERE devMac = cur_MAC
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Mark not present if not in CurrentScan
|
||||||
|
sql.execute("""
|
||||||
|
UPDATE Devices
|
||||||
|
SET devPresentLastScan = 0
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1 FROM CurrentScan
|
||||||
|
WHERE devMac = cur_MAC
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
def update_devLastConnection_from_CurrentScan(db):
|
||||||
|
"""
|
||||||
|
Update devLastConnection to current time for all devices seen in CurrentScan.
|
||||||
|
"""
|
||||||
|
sql = db.sql
|
||||||
startTime = timeNowDB()
|
startTime = timeNowDB()
|
||||||
|
mylog("debug", f"[Update Devices] - Updating devLastConnection to {startTime}")
|
||||||
|
|
||||||
device_columns = set()
|
sql.execute(f"""
|
||||||
try:
|
UPDATE Devices
|
||||||
device_columns = {row["name"] for row in sql.execute("PRAGMA table_info(Devices)").fetchall()}
|
SET devLastConnection = '{startTime}'
|
||||||
except Exception:
|
WHERE EXISTS (
|
||||||
device_columns = set()
|
SELECT 1 FROM CurrentScan
|
||||||
|
WHERE devMac = cur_MAC
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
def has_column(column_name):
|
|
||||||
return column_name in device_columns if device_columns else False
|
|
||||||
|
|
||||||
# Update Last Connection
|
def update_devices_data_from_scan(db):
|
||||||
mylog("debug", "[Update Devices] 1 Last Connection")
|
sql = db.sql
|
||||||
sql.execute(f"""UPDATE Devices SET devLastConnection = '{startTime}',
|
|
||||||
devPresentLastScan = 1
|
|
||||||
WHERE EXISTS (SELECT 1 FROM CurrentScan
|
|
||||||
WHERE devMac = cur_MAC) """)
|
|
||||||
|
|
||||||
# Clean no active devices
|
# ----------------------------------------------------------------
|
||||||
mylog("debug", "[Update Devices] 2 Clean no active devices")
|
# 1️⃣ Get plugin scan methods
|
||||||
sql.execute("""UPDATE Devices SET devPresentLastScan = 0
|
# ----------------------------------------------------------------
|
||||||
WHERE NOT EXISTS (SELECT 1 FROM CurrentScan
|
plugin_rows = sql.execute("SELECT DISTINCT cur_ScanMethod FROM CurrentScan").fetchall()
|
||||||
WHERE devMac = cur_MAC) """)
|
plugin_prefixes = [row[0] for row in plugin_rows if row[0]] or [None]
|
||||||
|
|
||||||
plugin_rows = sql.execute(
|
|
||||||
"SELECT DISTINCT cur_ScanMethod FROM CurrentScan"
|
|
||||||
).fetchall()
|
|
||||||
plugin_prefixes = [row[0] for row in plugin_rows if row[0]]
|
|
||||||
if not plugin_prefixes:
|
|
||||||
plugin_prefixes = [None]
|
|
||||||
plugin_settings_cache = {}
|
plugin_settings_cache = {}
|
||||||
|
|
||||||
def get_plugin_settings_cached(plugin_prefix):
|
def get_plugin_settings_cached(plugin_prefix):
|
||||||
if plugin_prefix not in plugin_settings_cache:
|
if plugin_prefix not in plugin_settings_cache:
|
||||||
plugin_settings_cache[plugin_prefix] = get_plugin_authoritative_settings(
|
plugin_settings_cache[plugin_prefix] = get_plugin_authoritative_settings(plugin_prefix)
|
||||||
plugin_prefix
|
|
||||||
)
|
|
||||||
return plugin_settings_cache[plugin_prefix]
|
return plugin_settings_cache[plugin_prefix]
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
# 2️⃣ Loop over plugins & update fields
|
||||||
|
# ----------------------------------------------------------------
|
||||||
for plugin_prefix in plugin_prefixes:
|
for plugin_prefix in plugin_prefixes:
|
||||||
filter_by_scan_method = plugin_prefix is not None and plugin_prefix != ""
|
filter_by_scan_method = bool(plugin_prefix)
|
||||||
source_prefix = plugin_prefix if filter_by_scan_method else "NEWDEV"
|
source_prefix = plugin_prefix if filter_by_scan_method else "NEWDEV"
|
||||||
plugin_settings = get_plugin_settings_cached(source_prefix)
|
plugin_settings = get_plugin_settings_cached(source_prefix)
|
||||||
|
|
||||||
has_last_ip_source = has_column("devLastIpSource")
|
# Get all devices joined with latest scan
|
||||||
has_vendor_source = has_column("devVendorSource")
|
sql_tmp = f"""
|
||||||
has_parent_port_source = has_column("devParentPortSource")
|
SELECT *
|
||||||
has_parent_mac_source = has_column("devParentMacSource")
|
FROM LatestDeviceScan
|
||||||
has_ssid_source = has_column("devSsidSource")
|
{"WHERE cur_ScanMethod = ?" if filter_by_scan_method else ""}
|
||||||
has_name_source = has_column("devNameSource")
|
"""
|
||||||
|
rows = sql.execute(sql_tmp, (source_prefix,) if filter_by_scan_method else ()).fetchall()
|
||||||
|
col_names = [desc[0] for desc in sql.description]
|
||||||
|
|
||||||
dev_last_ip_clause = (
|
for row in rows:
|
||||||
get_overwrite_sql_clause("devLastIP", "devLastIpSource", plugin_settings)
|
row_dict = dict(zip(col_names, row))
|
||||||
if has_last_ip_source
|
|
||||||
else "1=1"
|
for field, spec in FIELD_SPECS.items():
|
||||||
)
|
|
||||||
dev_vendor_clause = (
|
scan_col = spec.get("scan_col")
|
||||||
get_overwrite_sql_clause("devVendor", "devVendorSource", plugin_settings)
|
if scan_col not in row_dict:
|
||||||
if has_vendor_source
|
continue
|
||||||
else "1=1"
|
|
||||||
)
|
current_value = row_dict.get(field)
|
||||||
dev_parent_port_clause = (
|
current_source = row_dict.get(f"{field}Source") or ""
|
||||||
get_overwrite_sql_clause("devParentPort", "devParentPortSource", plugin_settings)
|
new_value = row_dict.get(scan_col)
|
||||||
if has_parent_port_source
|
|
||||||
else "1=1"
|
mylog("debug", f"[Update Devices] - current_value: {current_value} new_value: {new_value} -> {field}")
|
||||||
)
|
|
||||||
dev_parent_mac_clause = (
|
if can_overwrite_field(
|
||||||
get_overwrite_sql_clause("devParentMAC", "devParentMacSource", plugin_settings)
|
field_name=field,
|
||||||
if has_parent_mac_source
|
current_value=current_value,
|
||||||
else "1=1"
|
current_source=current_source,
|
||||||
)
|
plugin_prefix=source_prefix,
|
||||||
dev_ssid_clause = (
|
plugin_settings=plugin_settings,
|
||||||
get_overwrite_sql_clause("devSSID", "devSsidSource", plugin_settings)
|
field_value=new_value,
|
||||||
if has_ssid_source
|
):
|
||||||
else "1=1"
|
# Build UPDATE dynamically
|
||||||
)
|
update_cols = [f"{field} = ?"]
|
||||||
dev_name_clause = (
|
sql_val = [new_value]
|
||||||
get_overwrite_sql_clause("devName", "devNameSource", plugin_settings)
|
|
||||||
if has_name_source
|
# if a source field available, update too
|
||||||
else "1=1"
|
source_field = FIELD_SOURCE_MAP.get(field)
|
||||||
|
if source_field:
|
||||||
|
update_cols.append(f"{source_field} = ?")
|
||||||
|
sql_val.append(source_prefix)
|
||||||
|
|
||||||
|
sql_val.append(row_dict["devMac"])
|
||||||
|
|
||||||
|
sql_tmp = f"""
|
||||||
|
UPDATE Devices
|
||||||
|
SET {', '.join(update_cols)}
|
||||||
|
WHERE devMac = ?
|
||||||
|
"""
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
db.commitDB()
|
||||||
|
|
||||||
|
|
||||||
|
def update_ipv4_ipv6(db):
|
||||||
|
"""
|
||||||
|
Fill devPrimaryIPv4 and devPrimaryIPv6 based on devLastIP.
|
||||||
|
Skips empty devLastIP.
|
||||||
|
"""
|
||||||
|
sql = db.sql
|
||||||
|
|
||||||
|
mylog("debug", "[Update Devices] Updating devPrimaryIPv4 / devPrimaryIPv6 from devLastIP")
|
||||||
|
|
||||||
|
devices = sql.execute("SELECT devMac, devLastIP FROM Devices").fetchall()
|
||||||
|
records_to_update = []
|
||||||
|
|
||||||
|
for device in devices:
|
||||||
|
last_ip = device["devLastIP"]
|
||||||
|
if not last_ip or last_ip.lower() in ("", "null", "(unknown)", "(Unknown)"):
|
||||||
|
continue # skip empty
|
||||||
|
|
||||||
|
ipv4, ipv6 = None, None
|
||||||
|
try:
|
||||||
|
ip_obj = ipaddress.ip_address(last_ip)
|
||||||
|
if ip_obj.version == 4:
|
||||||
|
ipv4 = last_ip
|
||||||
|
else:
|
||||||
|
ipv6 = last_ip
|
||||||
|
except ValueError:
|
||||||
|
continue # invalid IP, skip
|
||||||
|
|
||||||
|
records_to_update.append([ipv4, ipv6, device["devMac"]])
|
||||||
|
|
||||||
|
if records_to_update:
|
||||||
|
sql.executemany(
|
||||||
|
"UPDATE Devices SET devPrimaryIPv4 = ?, devPrimaryIPv6 = ? WHERE devMac = ?",
|
||||||
|
records_to_update,
|
||||||
)
|
)
|
||||||
|
|
||||||
name_is_set_always = "devName" in plugin_settings.get("set_always", [])
|
mylog("debug", f"[Update Devices] Updated {len(records_to_update)} IPv4/IPv6 entries")
|
||||||
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 = (
|
|
||||||
SELECT cur_Vendor
|
|
||||||
FROM CurrentScan
|
|
||||||
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
|
|
||||||
)
|
|
||||||
{vendor_source_fragment}
|
|
||||||
WHERE {vendor_empty_condition}
|
|
||||||
AND EXISTS (
|
|
||||||
SELECT 1
|
|
||||||
FROM CurrentScan
|
|
||||||
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 parent port
|
|
||||||
mylog("debug", f"[Update Devices] - ({source_prefix}) cur_Port -> devParentPort")
|
|
||||||
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 = (
|
|
||||||
SELECT cur_Port
|
|
||||||
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')
|
|
||||||
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_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 parent MAC
|
|
||||||
mylog("debug", f"[Update Devices] - ({source_prefix}) cur_NetworkNodeMAC -> devParentMAC")
|
|
||||||
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 = (
|
|
||||||
SELECT cur_NetworkNodeMAC
|
|
||||||
FROM CurrentScan
|
|
||||||
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
|
|
||||||
)
|
|
||||||
{parent_mac_source_fragment}
|
|
||||||
WHERE {parent_mac_empty_condition}
|
|
||||||
AND EXISTS (
|
|
||||||
SELECT 1
|
|
||||||
FROM CurrentScan
|
|
||||||
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 {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
|
|
||||||
mylog("debug", "[Update Devices] - (if not empty) cur_NetworkSite -> (if empty) devSite",)
|
|
||||||
sql.execute("""UPDATE Devices
|
|
||||||
SET devSite = (
|
|
||||||
SELECT cur_NetworkSite
|
|
||||||
FROM CurrentScan
|
|
||||||
WHERE Devices.devMac = CurrentScan.cur_MAC
|
|
||||||
)
|
|
||||||
WHERE
|
|
||||||
(devSite IS NULL OR devSite IN ("", "null"))
|
|
||||||
AND EXISTS (
|
|
||||||
SELECT 1
|
|
||||||
FROM CurrentScan
|
|
||||||
WHERE Devices.devMac = CurrentScan.cur_MAC
|
|
||||||
AND CurrentScan.cur_NetworkSite IS NOT NULL AND CurrentScan.cur_NetworkSite NOT IN ("", "null")
|
|
||||||
)""")
|
|
||||||
|
|
||||||
# Update only devices with empty or NULL devType
|
|
||||||
mylog("debug", "[Update Devices] - (if not empty) cur_Type -> (if empty) devType")
|
|
||||||
sql.execute("""UPDATE Devices
|
|
||||||
SET devType = (
|
|
||||||
SELECT cur_Type
|
|
||||||
FROM CurrentScan
|
|
||||||
WHERE Devices.devMac = CurrentScan.cur_MAC
|
|
||||||
)
|
|
||||||
WHERE
|
|
||||||
(devType IS NULL OR devType IN ("", "null"))
|
|
||||||
AND EXISTS (
|
|
||||||
SELECT 1
|
|
||||||
FROM CurrentScan
|
|
||||||
WHERE Devices.devMac = CurrentScan.cur_MAC
|
|
||||||
AND CurrentScan.cur_Type IS NOT NULL AND CurrentScan.cur_Type NOT IN ("", "null")
|
|
||||||
)""")
|
|
||||||
|
|
||||||
# Update VENDORS
|
|
||||||
recordsToUpdate = []
|
|
||||||
vendor_settings = get_plugin_authoritative_settings("VNDRPDT")
|
|
||||||
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):
|
|
||||||
vendor = query_MAC_vendor(device["devMac"])
|
|
||||||
if vendor != -1 and vendor != -2:
|
|
||||||
recordsToUpdate.append([vendor, "VNDRPDT", device["devMac"]])
|
|
||||||
|
|
||||||
if len(recordsToUpdate) > 0:
|
|
||||||
if has_column("devVendorSource"):
|
|
||||||
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(db)
|
|
||||||
|
|
||||||
# Force device status if configured
|
|
||||||
update_devPresentLastScan_based_on_force_status(db)
|
|
||||||
|
|
||||||
|
def update_icons_and_types(db):
|
||||||
|
sql = db.sql
|
||||||
# Guess ICONS
|
# Guess ICONS
|
||||||
recordsToUpdate = []
|
recordsToUpdate = []
|
||||||
|
|
||||||
@@ -682,7 +425,62 @@ def update_devices_data_from_scan(db):
|
|||||||
"UPDATE Devices SET devType = ? WHERE devMac = ? ", recordsToUpdate
|
"UPDATE Devices SET devType = ? WHERE devMac = ? ", recordsToUpdate
|
||||||
)
|
)
|
||||||
|
|
||||||
mylog("debug", "[Update Devices] Update devices end")
|
|
||||||
|
def update_vendors_from_mac(db):
|
||||||
|
"""
|
||||||
|
Enrich Devices.devVendor using MAC vendor lookup (VNDRPDT),
|
||||||
|
without modifying CurrentScan. Respects plugin authoritative rules.
|
||||||
|
"""
|
||||||
|
sql = db.sql
|
||||||
|
recordsToUpdate = []
|
||||||
|
|
||||||
|
# Get plugin authoritative settings for vendor
|
||||||
|
vendor_settings = get_plugin_authoritative_settings("VNDRPDT")
|
||||||
|
vendor_clause = (
|
||||||
|
get_overwrite_sql_clause("devVendor", "devVendorSource", vendor_settings)
|
||||||
|
if has_column(sql, "devVendorSource")
|
||||||
|
else "1=1"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Build mapping: devMac -> vendor (skip unknown or invalid)
|
||||||
|
vendor_map = {}
|
||||||
|
for row in sql.execute("SELECT DISTINCT cur_MAC FROM CurrentScan"):
|
||||||
|
mac = row["cur_MAC"]
|
||||||
|
vendor = query_MAC_vendor(mac)
|
||||||
|
if vendor not in (-1, -2):
|
||||||
|
vendor_map[mac] = vendor
|
||||||
|
|
||||||
|
mylog("debug", f"[Vendor Mapping] Found {len(vendor_map)} valid MACs to enrich")
|
||||||
|
|
||||||
|
# Select Devices eligible for vendor update
|
||||||
|
if "devVendor" in vendor_settings.get("set_always", []):
|
||||||
|
# Always overwrite eligible devices
|
||||||
|
query = f"SELECT devMac FROM Devices WHERE {vendor_clause}"
|
||||||
|
else:
|
||||||
|
# Only update empty or unknown vendors
|
||||||
|
empty_vals = FIELD_SPECS.get("devVendor", {}).get("empty_values", [])
|
||||||
|
empty_condition = " OR ".join(f"devVendor = '{v}'" for v in empty_vals)
|
||||||
|
query = f"SELECT devMac FROM Devices WHERE ({empty_condition} OR devVendor IS NULL) AND {vendor_clause}"
|
||||||
|
|
||||||
|
for device in sql.execute(query):
|
||||||
|
mac = device["devMac"]
|
||||||
|
if mac in vendor_map:
|
||||||
|
recordsToUpdate.append([vendor_map[mac], "VNDRPDT", mac])
|
||||||
|
|
||||||
|
# Apply updates
|
||||||
|
if recordsToUpdate:
|
||||||
|
if has_column(sql, "devVendorSource"):
|
||||||
|
sql.executemany(
|
||||||
|
"UPDATE Devices SET devVendor = ?, devVendorSource = ? WHERE devMac = ? AND " + vendor_clause,
|
||||||
|
recordsToUpdate,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
sql.executemany(
|
||||||
|
"UPDATE Devices SET devVendor = ? WHERE devMac = ?",
|
||||||
|
[(r[0], r[2]) for r in recordsToUpdate],
|
||||||
|
)
|
||||||
|
|
||||||
|
mylog("debug", f"[Update Devices] Updated {len(recordsToUpdate)} vendors using MAC mapping")
|
||||||
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -4,6 +4,13 @@ from scan.device_handling import (
|
|||||||
save_scanned_devices,
|
save_scanned_devices,
|
||||||
exclude_ignored_devices,
|
exclude_ignored_devices,
|
||||||
update_devices_data_from_scan,
|
update_devices_data_from_scan,
|
||||||
|
update_vendors_from_mac,
|
||||||
|
update_icons_and_types,
|
||||||
|
update_devPresentLastScan_based_on_force_status,
|
||||||
|
update_devPresentLastScan_based_on_nics,
|
||||||
|
update_ipv4_ipv6,
|
||||||
|
update_devLastConnection_from_CurrentScan,
|
||||||
|
update_presence_from_CurrentScan
|
||||||
)
|
)
|
||||||
from helper import get_setting_value
|
from helper import get_setting_value
|
||||||
from db.db_helper import print_table_schema
|
from db.db_helper import print_table_schema
|
||||||
@@ -49,6 +56,34 @@ def process_scan(db):
|
|||||||
mylog("verbose", "[Process Scan] Updating Devices Info")
|
mylog("verbose", "[Process Scan] Updating Devices Info")
|
||||||
update_devices_data_from_scan(db)
|
update_devices_data_from_scan(db)
|
||||||
|
|
||||||
|
# Last Connection Time stamp from CurrentSan
|
||||||
|
mylog("verbose", "[Process Scan] Updating devLastConnection from CurrentSan")
|
||||||
|
update_devLastConnection_from_CurrentScan(db)
|
||||||
|
|
||||||
|
# Presence from CurrentSan
|
||||||
|
mylog("verbose", "[Process Scan] Updating Devices Info")
|
||||||
|
update_presence_from_CurrentScan(db)
|
||||||
|
|
||||||
|
# Update devPresentLastScan based on NICs presence
|
||||||
|
mylog("verbose", "[Process Scan] Updating NICs presence")
|
||||||
|
update_devPresentLastScan_based_on_nics(db)
|
||||||
|
|
||||||
|
# Force device status
|
||||||
|
mylog("verbose", "[Process Scan] Updating forced presence")
|
||||||
|
update_devPresentLastScan_based_on_force_status(db)
|
||||||
|
|
||||||
|
# Update Vendors
|
||||||
|
mylog("verbose", "[Process Scan] Updating Vendors")
|
||||||
|
update_vendors_from_mac(db)
|
||||||
|
|
||||||
|
# Update IPs
|
||||||
|
mylog("verbose", "[Process Scan] Updating v4 and v6 IPs")
|
||||||
|
update_ipv4_ipv6(db)
|
||||||
|
|
||||||
|
# Update Icons and Type based on heuristics
|
||||||
|
mylog("verbose", "[Process Scan] Guessing Icons")
|
||||||
|
update_icons_and_types(db)
|
||||||
|
|
||||||
# Pair session events (Connection / Disconnection)
|
# Pair session events (Connection / Disconnection)
|
||||||
mylog("verbose", "[Process Scan] Pairing session events (connection / disconnection) ")
|
mylog("verbose", "[Process Scan] Pairing session events (connection / disconnection) ")
|
||||||
pair_sessions_events(db)
|
pair_sessions_events(db)
|
||||||
@@ -67,7 +102,7 @@ def process_scan(db):
|
|||||||
|
|
||||||
# Clear current scan as processed
|
# Clear current scan as processed
|
||||||
# 🐛 CurrentScan DEBUG: comment out below when debugging to keep the CurrentScan table after restarts/scan finishes
|
# 🐛 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")
|
||||||
|
|
||||||
# Commit changes
|
# Commit changes
|
||||||
db.commitDB()
|
db.commitDB()
|
||||||
|
|||||||
@@ -156,14 +156,21 @@ def parse_datetime(dt_str):
|
|||||||
|
|
||||||
def format_date(date_str: str) -> str:
|
def format_date(date_str: str) -> str:
|
||||||
try:
|
try:
|
||||||
|
if isinstance(date_str, str):
|
||||||
|
# collapse all whitespace into single spaces
|
||||||
|
date_str = re.sub(r"\s+", " ", date_str.strip())
|
||||||
|
|
||||||
dt = parse_datetime(date_str)
|
dt = parse_datetime(date_str)
|
||||||
|
if not dt:
|
||||||
|
return f"invalid:{repr(date_str)}"
|
||||||
|
|
||||||
if dt.tzinfo is None:
|
if dt.tzinfo is None:
|
||||||
# Set timezone if missing — change to timezone.utc if you prefer UTC
|
dt = dt.replace(tzinfo=conf.tz)
|
||||||
now = datetime.datetime.now(conf.tz)
|
|
||||||
dt = dt.replace(tzinfo=now.astimezone().tzinfo)
|
|
||||||
return dt.astimezone().isoformat()
|
return dt.astimezone().isoformat()
|
||||||
except (ValueError, AttributeError, TypeError):
|
|
||||||
return "invalid"
|
except Exception:
|
||||||
|
return f"invalid:{repr(date_str)}"
|
||||||
|
|
||||||
|
|
||||||
def format_date_diff(date1, date2, tz_name):
|
def format_date_diff(date1, date2, tz_name):
|
||||||
|
|||||||
Reference in New Issue
Block a user