mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2025-12-07 09:36:05 -08:00
/data and /tmp standarization
This commit is contained in:
@@ -1,12 +1,11 @@
|
||||
import sys
|
||||
import subprocess
|
||||
import conf
|
||||
import os
|
||||
import re
|
||||
from dateutil import parser
|
||||
|
||||
# Register NetAlertX directories
|
||||
INSTALL_PATH="/app"
|
||||
INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
|
||||
sys.path.extend([f"{INSTALL_PATH}/server"])
|
||||
|
||||
from helper import timeNowTZ, get_setting_value, check_IP_format
|
||||
@@ -18,15 +17,20 @@ 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'))
|
||||
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):
|
||||
sql = db.sql # Database interface for executing queries
|
||||
|
||||
mac_condition = list_to_where('OR', 'cur_MAC', 'LIKE', get_setting_value('NEWDEV_ignored_MACs'))
|
||||
ip_condition = list_to_where('OR', 'cur_IP', 'LIKE', get_setting_value('NEWDEV_ignored_IPs'))
|
||||
mac_condition = list_to_where(
|
||||
"OR", "cur_MAC", "LIKE", get_setting_value("NEWDEV_ignored_MACs")
|
||||
)
|
||||
ip_condition = list_to_where(
|
||||
"OR", "cur_IP", "LIKE", get_setting_value("NEWDEV_ignored_IPs")
|
||||
)
|
||||
|
||||
# Only delete if either the MAC or IP matches an ignored condition
|
||||
conditions = []
|
||||
@@ -47,31 +51,31 @@ def exclude_ignored_devices(db):
|
||||
else:
|
||||
query = "DELETE FROM CurrentScan WHERE 1=1 AND 1=0" # No valid conditions, prevent deletion
|
||||
|
||||
mylog('debug', f'[New Devices] Excluding Ignored Devices Query: {query}')
|
||||
mylog("debug", f"[New Devices] Excluding Ignored Devices Query: {query}")
|
||||
|
||||
sql.execute(query)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def update_devices_data_from_scan (db):
|
||||
sql = db.sql #TO-DO
|
||||
startTime = timeNowTZ().strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
def update_devices_data_from_scan(db):
|
||||
sql = db.sql # TO-DO
|
||||
startTime = timeNowTZ().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
# 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}',
|
||||
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')
|
||||
mylog("debug", "[Update Devices] 2 Clean no active devices")
|
||||
sql.execute("""UPDATE Devices SET devPresentLastScan = 0
|
||||
WHERE NOT EXISTS (SELECT 1 FROM CurrentScan
|
||||
WHERE devMac = cur_MAC) """)
|
||||
|
||||
# Update IP
|
||||
mylog('debug', '[Update Devices] - cur_IP -> devLastIP (always updated)')
|
||||
# Update IP
|
||||
mylog("debug", "[Update Devices] - cur_IP -> devLastIP (always updated)")
|
||||
sql.execute("""UPDATE Devices
|
||||
SET devLastIP = (
|
||||
SELECT cur_IP
|
||||
@@ -90,9 +94,8 @@ def update_devices_data_from_scan (db):
|
||||
AND cur_IP NOT IN ('', 'null', '(unknown)', '(Unknown)')
|
||||
)""")
|
||||
|
||||
|
||||
# Update only devices with empty, NULL or (u(U)nknown) vendors
|
||||
mylog('debug', '[Update Devices] - cur_Vendor -> (if empty) devVendor')
|
||||
mylog("debug", "[Update Devices] - cur_Vendor -> (if empty) devVendor")
|
||||
sql.execute("""UPDATE Devices
|
||||
SET devVendor = (
|
||||
SELECT cur_Vendor
|
||||
@@ -107,8 +110,8 @@ def update_devices_data_from_scan (db):
|
||||
WHERE Devices.devMac = CurrentScan.cur_MAC
|
||||
)""")
|
||||
|
||||
# Update only devices with empty or NULL devParentPort
|
||||
mylog('debug', '[Update Devices] - (if not empty) cur_Port -> devParentPort')
|
||||
# Update only devices with empty or NULL devParentPort
|
||||
mylog("debug", "[Update Devices] - (if not empty) cur_Port -> devParentPort")
|
||||
sql.execute("""UPDATE Devices
|
||||
SET devParentPort = (
|
||||
SELECT cur_Port
|
||||
@@ -125,8 +128,10 @@ def update_devices_data_from_scan (db):
|
||||
AND CurrentScan.cur_Port IS NOT NULL AND CurrentScan.cur_Port NOT IN ("", "null")
|
||||
)""")
|
||||
|
||||
# Update only devices with empty or NULL devParentMAC
|
||||
mylog('debug', '[Update Devices] - (if not empty) cur_NetworkNodeMAC -> devParentMAC')
|
||||
# Update only devices with empty or NULL devParentMAC
|
||||
mylog(
|
||||
"debug", "[Update Devices] - (if not empty) cur_NetworkNodeMAC -> devParentMAC"
|
||||
)
|
||||
sql.execute("""UPDATE Devices
|
||||
SET devParentMAC = (
|
||||
SELECT cur_NetworkNodeMAC
|
||||
@@ -144,9 +149,11 @@ def update_devices_data_from_scan (db):
|
||||
)
|
||||
""")
|
||||
|
||||
|
||||
# Update only devices with empty or NULL devSite
|
||||
mylog('debug', '[Update Devices] - (if not empty) cur_NetworkSite -> (if empty) devSite')
|
||||
# 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
|
||||
@@ -162,8 +169,8 @@ def update_devices_data_from_scan (db):
|
||||
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')
|
||||
# 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
|
||||
@@ -180,7 +187,7 @@ def update_devices_data_from_scan (db):
|
||||
)""")
|
||||
|
||||
# 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
|
||||
SET devType = (
|
||||
SELECT cur_Type
|
||||
@@ -197,8 +204,8 @@ def update_devices_data_from_scan (db):
|
||||
)""")
|
||||
|
||||
# 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
|
||||
mylog("debug", "[Update Devices] - (if not empty) cur_Name -> (if empty) devName")
|
||||
sql.execute(""" UPDATE Devices
|
||||
SET devName = COALESCE((
|
||||
SELECT cur_Name
|
||||
FROM CurrentScan
|
||||
@@ -224,23 +231,25 @@ def update_devices_data_from_scan (db):
|
||||
WHERE devVendor IS NULL OR devVendor IN ("", "null", "(unknown)", "(Unknown)")
|
||||
"""
|
||||
|
||||
for device in sql.execute (query) :
|
||||
vendor = query_MAC_vendor (device['devMac'])
|
||||
if vendor != -1 and vendor != -2 :
|
||||
recordsToUpdate.append ([vendor, device['devMac']])
|
||||
for device in sql.execute(query):
|
||||
vendor = query_MAC_vendor(device["devMac"])
|
||||
if vendor != -1 and vendor != -2:
|
||||
recordsToUpdate.append([vendor, device["devMac"]])
|
||||
|
||||
if len(recordsToUpdate) > 0:
|
||||
sql.executemany ("UPDATE Devices SET devVendor = ? WHERE devMac = ? ", recordsToUpdate )
|
||||
if len(recordsToUpdate) > 0:
|
||||
sql.executemany(
|
||||
"UPDATE Devices SET devVendor = ? WHERE devMac = ? ", recordsToUpdate
|
||||
)
|
||||
|
||||
# Update devPresentLastScan based on NICs presence
|
||||
update_devPresentLastScan_based_on_nics(db)
|
||||
|
||||
|
||||
# Guess ICONS
|
||||
recordsToUpdate = []
|
||||
|
||||
default_icon = get_setting_value('NEWDEV_devIcon')
|
||||
default_icon = get_setting_value("NEWDEV_devIcon")
|
||||
|
||||
if get_setting_value('NEWDEV_replace_preset_icon'):
|
||||
if get_setting_value("NEWDEV_replace_preset_icon"):
|
||||
query = f"""SELECT * FROM Devices
|
||||
WHERE devIcon in ('', 'null', '{default_icon}')
|
||||
OR devIcon IS NULL"""
|
||||
@@ -248,62 +257,97 @@ def update_devices_data_from_scan (db):
|
||||
query = """SELECT * FROM Devices
|
||||
WHERE devIcon in ('', 'null')
|
||||
OR devIcon IS NULL"""
|
||||
|
||||
for device in sql.execute (query) :
|
||||
# Conditional logic for devIcon guessing
|
||||
devIcon = guess_icon(device['devVendor'], device['devMac'], device['devLastIP'], device['devName'], default_icon)
|
||||
|
||||
recordsToUpdate.append ([devIcon, device['devMac']])
|
||||
for device in sql.execute(query):
|
||||
# Conditional logic for devIcon guessing
|
||||
devIcon = guess_icon(
|
||||
device["devVendor"],
|
||||
device["devMac"],
|
||||
device["devLastIP"],
|
||||
device["devName"],
|
||||
default_icon,
|
||||
)
|
||||
|
||||
recordsToUpdate.append([devIcon, device["devMac"]])
|
||||
|
||||
mylog('debug',f'[Update Devices] recordsToUpdate: {recordsToUpdate}')
|
||||
|
||||
if len(recordsToUpdate) > 0:
|
||||
sql.executemany ("UPDATE Devices SET devIcon = ? WHERE devMac = ? ", recordsToUpdate )
|
||||
mylog("debug", f"[Update Devices] recordsToUpdate: {recordsToUpdate}")
|
||||
|
||||
if len(recordsToUpdate) > 0:
|
||||
sql.executemany(
|
||||
"UPDATE Devices SET devIcon = ? WHERE devMac = ? ", recordsToUpdate
|
||||
)
|
||||
|
||||
# Guess Type
|
||||
recordsToUpdate = []
|
||||
query = """SELECT * FROM Devices
|
||||
WHERE devType in ('', 'null')
|
||||
OR devType IS NULL"""
|
||||
default_type = get_setting_value('NEWDEV_devType')
|
||||
|
||||
for device in sql.execute (query) :
|
||||
# Conditional logic for devIcon guessing
|
||||
devType = guess_type(device['devVendor'], device['devMac'], device['devLastIP'], device['devName'], default_type)
|
||||
default_type = get_setting_value("NEWDEV_devType")
|
||||
|
||||
recordsToUpdate.append ([devType, device['devMac']])
|
||||
|
||||
if len(recordsToUpdate) > 0:
|
||||
sql.executemany ("UPDATE Devices SET devType = ? WHERE devMac = ? ", recordsToUpdate )
|
||||
|
||||
|
||||
mylog('debug','[Update Devices] Update devices end')
|
||||
for device in sql.execute(query):
|
||||
# Conditional logic for devIcon guessing
|
||||
devType = guess_type(
|
||||
device["devVendor"],
|
||||
device["devMac"],
|
||||
device["devLastIP"],
|
||||
device["devName"],
|
||||
default_type,
|
||||
)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def save_scanned_devices (db):
|
||||
sql = db.sql #TO-DO
|
||||
recordsToUpdate.append([devType, device["devMac"]])
|
||||
|
||||
if len(recordsToUpdate) > 0:
|
||||
sql.executemany(
|
||||
"UPDATE Devices SET devType = ? WHERE devMac = ? ", recordsToUpdate
|
||||
)
|
||||
|
||||
mylog("debug", "[Update Devices] Update devices end")
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
def save_scanned_devices(db):
|
||||
sql = db.sql # TO-DO
|
||||
|
||||
# Add Local MAC of default local interface
|
||||
local_mac_cmd = ["/sbin/ifconfig `ip -o route get 1 | sed 's/^.*dev \\([^ ]*\\).*$/\\1/;q'` | grep ether | awk '{print $2}'"]
|
||||
local_mac = subprocess.Popen (local_mac_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0].decode().strip()
|
||||
local_mac_cmd = [
|
||||
"/sbin/ifconfig `ip -o route get 1 | sed 's/^.*dev \\([^ ]*\\).*$/\\1/;q'` | grep ether | awk '{print $2}'"
|
||||
]
|
||||
local_mac = (
|
||||
subprocess.Popen(
|
||||
local_mac_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
||||
)
|
||||
.communicate()[0]
|
||||
.decode()
|
||||
.strip()
|
||||
)
|
||||
|
||||
local_ip_cmd = ["ip -o route get 1 | sed 's/^.*src \\([^ ]*\\).*$/\\1/;q'"]
|
||||
local_ip = subprocess.Popen (local_ip_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0].decode().strip()
|
||||
local_ip = (
|
||||
subprocess.Popen(
|
||||
local_ip_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
||||
)
|
||||
.communicate()[0]
|
||||
.decode()
|
||||
.strip()
|
||||
)
|
||||
|
||||
mylog('debug', ['[Save Devices] Saving this IP into the CurrentScan table:', local_ip])
|
||||
mylog(
|
||||
"debug", ["[Save Devices] Saving this IP into the CurrentScan table:", local_ip]
|
||||
)
|
||||
|
||||
if check_IP_format(local_ip) == '':
|
||||
local_ip = '0.0.0.0'
|
||||
if check_IP_format(local_ip) == "":
|
||||
local_ip = "0.0.0.0"
|
||||
|
||||
# Proceed if variable contains valid MAC
|
||||
if check_mac_or_internet(local_mac):
|
||||
sql.execute (f"""INSERT OR IGNORE INTO CurrentScan (cur_MAC, cur_IP, cur_Vendor, cur_ScanMethod) VALUES ( '{local_mac}', '{local_ip}', Null, 'local_MAC') """)
|
||||
sql.execute(
|
||||
f"""INSERT OR IGNORE INTO CurrentScan (cur_MAC, cur_IP, cur_Vendor, cur_ScanMethod) VALUES ( '{local_mac}', '{local_ip}', Null, 'local_MAC') """
|
||||
)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
def print_scan_stats(db):
|
||||
sql = db.sql # TO-DO
|
||||
sql = db.sql # TO-DO
|
||||
|
||||
query = """
|
||||
SELECT
|
||||
@@ -323,59 +367,71 @@ def print_scan_stats(db):
|
||||
sql.execute(query)
|
||||
stats = sql.fetchall()
|
||||
|
||||
mylog('verbose', f'[Scan Stats] Devices Detected.......: {stats[0]["devices_detected"]}')
|
||||
mylog('verbose', f'[Scan Stats] New Devices............: {stats[0]["new_devices"]}')
|
||||
mylog('verbose', f'[Scan Stats] Down Alerts............: {stats[0]["down_alerts"]}')
|
||||
mylog('verbose', f'[Scan Stats] New Down Alerts........: {stats[0]["new_down_alerts"]}')
|
||||
mylog('verbose', f'[Scan Stats] New Connections........: {stats[0]["new_connections"]}')
|
||||
mylog('verbose', f'[Scan Stats] Disconnections.........: {stats[0]["disconnections"]}')
|
||||
mylog('verbose', f'[Scan Stats] IP Changes.............: {stats[0]["ip_changes"]}')
|
||||
mylog(
|
||||
"verbose",
|
||||
f"[Scan Stats] Devices Detected.......: {stats[0]['devices_detected']}",
|
||||
)
|
||||
mylog("verbose", f"[Scan Stats] New Devices............: {stats[0]['new_devices']}")
|
||||
mylog("verbose", f"[Scan Stats] Down Alerts............: {stats[0]['down_alerts']}")
|
||||
mylog(
|
||||
"verbose",
|
||||
f"[Scan Stats] New Down Alerts........: {stats[0]['new_down_alerts']}",
|
||||
)
|
||||
mylog(
|
||||
"verbose",
|
||||
f"[Scan Stats] New Connections........: {stats[0]['new_connections']}",
|
||||
)
|
||||
mylog(
|
||||
"verbose", f"[Scan Stats] Disconnections.........: {stats[0]['disconnections']}"
|
||||
)
|
||||
mylog("verbose", f"[Scan Stats] IP Changes.............: {stats[0]['ip_changes']}")
|
||||
|
||||
# if str(stats[0]["new_devices"]) != '0':
|
||||
mylog('trace', f' ================ DEVICES table content ================')
|
||||
sql.execute('select * from Devices')
|
||||
mylog("trace", " ================ DEVICES table content ================")
|
||||
sql.execute("select * from Devices")
|
||||
rows = sql.fetchall()
|
||||
for row in rows:
|
||||
row_dict = dict(row)
|
||||
mylog('trace', f' {row_dict}')
|
||||
|
||||
mylog('trace', f' ================ CurrentScan table content ================')
|
||||
sql.execute('select * from CurrentScan')
|
||||
rows = sql.fetchall()
|
||||
for row in rows:
|
||||
row_dict = dict(row)
|
||||
mylog('trace', f' {row_dict}')
|
||||
|
||||
mylog('trace', f' ================ Events table content where eve_PendingAlertEmail = 1 ================')
|
||||
sql.execute('select * from Events where eve_PendingAlertEmail = 1')
|
||||
rows = sql.fetchall()
|
||||
for row in rows:
|
||||
row_dict = dict(row)
|
||||
mylog('trace', f' {row_dict}')
|
||||
mylog("trace", f" {row_dict}")
|
||||
|
||||
mylog('trace', f' ================ Events table COUNT ================')
|
||||
sql.execute('select count(*) from Events')
|
||||
mylog("trace", " ================ CurrentScan table content ================")
|
||||
sql.execute("select * from CurrentScan")
|
||||
rows = sql.fetchall()
|
||||
for row in rows:
|
||||
row_dict = dict(row)
|
||||
mylog('trace', f' {row_dict}')
|
||||
|
||||
mylog("trace", f" {row_dict}")
|
||||
|
||||
mylog('verbose', '[Scan Stats] Scan Method Statistics:')
|
||||
mylog(
|
||||
"trace",
|
||||
" ================ Events table content where eve_PendingAlertEmail = 1 ================",
|
||||
)
|
||||
sql.execute("select * from Events where eve_PendingAlertEmail = 1")
|
||||
rows = sql.fetchall()
|
||||
for row in rows:
|
||||
row_dict = dict(row)
|
||||
mylog("trace", f" {row_dict}")
|
||||
|
||||
mylog("trace", " ================ Events table COUNT ================")
|
||||
sql.execute("select count(*) from Events")
|
||||
rows = sql.fetchall()
|
||||
for row in rows:
|
||||
row_dict = dict(row)
|
||||
mylog("trace", f" {row_dict}")
|
||||
|
||||
mylog("verbose", "[Scan Stats] Scan Method Statistics:")
|
||||
for row in stats:
|
||||
if row["cur_ScanMethod"] is not None:
|
||||
mylog('verbose', f' {row["cur_ScanMethod"]}: {row["scan_method_count"]}')
|
||||
mylog("verbose", f" {row['cur_ScanMethod']}: {row['scan_method_count']}")
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def create_new_devices (db):
|
||||
sql = db.sql # TO-DO
|
||||
# -------------------------------------------------------------------------------
|
||||
def create_new_devices(db):
|
||||
sql = db.sql # TO-DO
|
||||
startTime = timeNowTZ()
|
||||
|
||||
# Insert events for new devices from CurrentScan (not yet in Devices)
|
||||
|
||||
|
||||
mylog('debug', '[New Devices] Insert "New Device" Events')
|
||||
mylog("debug", '[New Devices] Insert "New Device" Events')
|
||||
query_new_device_events = f"""
|
||||
INSERT INTO Events (
|
||||
eve_MAC, eve_IP, eve_DateTime,
|
||||
@@ -389,14 +445,14 @@ def create_new_devices (db):
|
||||
WHERE devMac = cur_MAC
|
||||
)
|
||||
"""
|
||||
|
||||
|
||||
# mylog('debug',f'[New Devices] Log Events Query: {query_new_device_events}')
|
||||
|
||||
|
||||
sql.execute(query_new_device_events)
|
||||
|
||||
mylog('debug',f'[New Devices] Insert Connection into session table')
|
||||
mylog("debug", "[New Devices] Insert Connection into session table")
|
||||
|
||||
sql.execute (f"""INSERT INTO Sessions (
|
||||
sql.execute(f"""INSERT INTO Sessions (
|
||||
ses_MAC, ses_IP, ses_EventTypeConnection, ses_DateTimeConnection,
|
||||
ses_EventTypeDisconnection, ses_DateTimeDisconnection,
|
||||
ses_StillConnected, ses_AdditionalInfo
|
||||
@@ -412,12 +468,12 @@ def create_new_devices (db):
|
||||
WHERE ses_MAC = cur_MAC AND ses_StillConnected = 1
|
||||
)
|
||||
""")
|
||||
|
||||
|
||||
# Create new devices from CurrentScan
|
||||
mylog('debug','[New Devices] 2 Create devices')
|
||||
mylog("debug", "[New Devices] 2 Create devices")
|
||||
|
||||
# default New Device values preparation
|
||||
newDevColumns = """devAlertEvents,
|
||||
newDevColumns = """devAlertEvents,
|
||||
devAlertDown,
|
||||
devPresentLastScan,
|
||||
devIsArchived,
|
||||
@@ -435,41 +491,66 @@ def create_new_devices (db):
|
||||
devReqNicsOnline
|
||||
"""
|
||||
|
||||
newDevDefaults = f"""{get_setting_value('NEWDEV_devAlertEvents')},
|
||||
{get_setting_value('NEWDEV_devAlertDown')},
|
||||
{get_setting_value('NEWDEV_devPresentLastScan')},
|
||||
{get_setting_value('NEWDEV_devIsArchived')},
|
||||
{get_setting_value('NEWDEV_devIsNew')},
|
||||
{get_setting_value('NEWDEV_devSkipRepeated')},
|
||||
{get_setting_value('NEWDEV_devScan')},
|
||||
'{sanitize_SQL_input(get_setting_value('NEWDEV_devOwner'))}',
|
||||
{get_setting_value('NEWDEV_devFavorite')},
|
||||
'{sanitize_SQL_input(get_setting_value('NEWDEV_devGroup'))}',
|
||||
'{sanitize_SQL_input(get_setting_value('NEWDEV_devComments'))}',
|
||||
{get_setting_value('NEWDEV_devLogEvents')},
|
||||
'{sanitize_SQL_input(get_setting_value('NEWDEV_devLocation'))}',
|
||||
'{sanitize_SQL_input(get_setting_value('NEWDEV_devCustomProps'))}',
|
||||
'{sanitize_SQL_input(get_setting_value('NEWDEV_devParentRelType'))}',
|
||||
{sanitize_SQL_input(get_setting_value('NEWDEV_devReqNicsOnline'))}
|
||||
newDevDefaults = f"""{get_setting_value("NEWDEV_devAlertEvents")},
|
||||
{get_setting_value("NEWDEV_devAlertDown")},
|
||||
{get_setting_value("NEWDEV_devPresentLastScan")},
|
||||
{get_setting_value("NEWDEV_devIsArchived")},
|
||||
{get_setting_value("NEWDEV_devIsNew")},
|
||||
{get_setting_value("NEWDEV_devSkipRepeated")},
|
||||
{get_setting_value("NEWDEV_devScan")},
|
||||
'{sanitize_SQL_input(get_setting_value("NEWDEV_devOwner"))}',
|
||||
{get_setting_value("NEWDEV_devFavorite")},
|
||||
'{sanitize_SQL_input(get_setting_value("NEWDEV_devGroup"))}',
|
||||
'{sanitize_SQL_input(get_setting_value("NEWDEV_devComments"))}',
|
||||
{get_setting_value("NEWDEV_devLogEvents")},
|
||||
'{sanitize_SQL_input(get_setting_value("NEWDEV_devLocation"))}',
|
||||
'{sanitize_SQL_input(get_setting_value("NEWDEV_devCustomProps"))}',
|
||||
'{sanitize_SQL_input(get_setting_value("NEWDEV_devParentRelType"))}',
|
||||
{sanitize_SQL_input(get_setting_value("NEWDEV_devReqNicsOnline"))}
|
||||
"""
|
||||
|
||||
# Fetch data from CurrentScan skipping ignored devices by IP and MAC
|
||||
query = f"""SELECT cur_MAC, cur_Name, cur_Vendor, cur_ScanMethod, cur_IP, cur_SyncHubNodeName, cur_NetworkNodeMAC, cur_PORT, cur_NetworkSite, cur_SSID, cur_Type
|
||||
FROM CurrentScan """
|
||||
query = """SELECT cur_MAC, cur_Name, cur_Vendor, cur_ScanMethod, cur_IP, cur_SyncHubNodeName, cur_NetworkNodeMAC, cur_PORT, cur_NetworkSite, cur_SSID, cur_Type
|
||||
FROM CurrentScan """
|
||||
|
||||
|
||||
mylog('debug',f'[New Devices] Collecting New Devices Query: {query}')
|
||||
mylog("debug", f"[New Devices] Collecting New Devices Query: {query}")
|
||||
current_scan_data = sql.execute(query).fetchall()
|
||||
|
||||
for row in current_scan_data:
|
||||
cur_MAC, cur_Name, cur_Vendor, cur_ScanMethod, cur_IP, cur_SyncHubNodeName, cur_NetworkNodeMAC, cur_PORT, cur_NetworkSite, cur_SSID, cur_Type = row
|
||||
(
|
||||
cur_MAC,
|
||||
cur_Name,
|
||||
cur_Vendor,
|
||||
cur_ScanMethod,
|
||||
cur_IP,
|
||||
cur_SyncHubNodeName,
|
||||
cur_NetworkNodeMAC,
|
||||
cur_PORT,
|
||||
cur_NetworkSite,
|
||||
cur_SSID,
|
||||
cur_Type,
|
||||
) = row
|
||||
|
||||
# Handle NoneType
|
||||
cur_Name = str(cur_Name).strip() if cur_Name else '(unknown)'
|
||||
cur_Type = str(cur_Type).strip() if cur_Type else get_setting_value("NEWDEV_devType")
|
||||
cur_NetworkNodeMAC = cur_NetworkNodeMAC.strip() if cur_NetworkNodeMAC else ''
|
||||
cur_NetworkNodeMAC = cur_NetworkNodeMAC if cur_NetworkNodeMAC and cur_MAC != "Internet" else (get_setting_value("NEWDEV_devParentMAC") if cur_MAC != "Internet" else "null")
|
||||
cur_SyncHubNodeName = cur_SyncHubNodeName if cur_SyncHubNodeName and cur_SyncHubNodeName != "null" else (get_setting_value("SYNC_node_name"))
|
||||
cur_Name = str(cur_Name).strip() if cur_Name else "(unknown)"
|
||||
cur_Type = (
|
||||
str(cur_Type).strip() if cur_Type else get_setting_value("NEWDEV_devType")
|
||||
)
|
||||
cur_NetworkNodeMAC = cur_NetworkNodeMAC.strip() if cur_NetworkNodeMAC else ""
|
||||
cur_NetworkNodeMAC = (
|
||||
cur_NetworkNodeMAC
|
||||
if cur_NetworkNodeMAC and cur_MAC != "Internet"
|
||||
else (
|
||||
get_setting_value("NEWDEV_devParentMAC")
|
||||
if cur_MAC != "Internet"
|
||||
else "null"
|
||||
)
|
||||
)
|
||||
cur_SyncHubNodeName = (
|
||||
cur_SyncHubNodeName
|
||||
if cur_SyncHubNodeName and cur_SyncHubNodeName != "null"
|
||||
else (get_setting_value("SYNC_node_name"))
|
||||
)
|
||||
|
||||
# Preparing the individual insert statement
|
||||
sqlQuery = f"""INSERT OR IGNORE INTO Devices
|
||||
@@ -509,17 +590,15 @@ def create_new_devices (db):
|
||||
{newDevDefaults}
|
||||
)"""
|
||||
|
||||
mylog('trace', f'[New Devices] Create device SQL: {sqlQuery}')
|
||||
mylog("trace", f"[New Devices] Create device SQL: {sqlQuery}")
|
||||
|
||||
sql.execute(sqlQuery, (startTime, startTime))
|
||||
|
||||
|
||||
mylog('debug','[New Devices] New Devices end')
|
||||
mylog("debug", "[New Devices] New Devices end")
|
||||
db.commitDB()
|
||||
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------------
|
||||
def update_devices_names(pm):
|
||||
sql = pm.db.sql
|
||||
resolver = NameResolver(pm.db)
|
||||
@@ -530,7 +609,11 @@ def update_devices_names(pm):
|
||||
|
||||
# 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
|
||||
last_checked_dt = (
|
||||
parser.parse(last_checked_str)
|
||||
if isinstance(last_checked_str, str)
|
||||
else last_checked_str
|
||||
)
|
||||
|
||||
# Collect valid state update timestamps for name-related plugins
|
||||
state_times = []
|
||||
@@ -545,28 +628,31 @@ def update_devices_names(pm):
|
||||
|
||||
# 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.')
|
||||
mylog(
|
||||
"debug",
|
||||
"[Update Device Name] No relevant name plugin changes since last check — skipping update.",
|
||||
)
|
||||
return
|
||||
|
||||
nameNotFound = "(name not found)"
|
||||
|
||||
# Define resolution strategies in priority order
|
||||
strategies = [
|
||||
(resolver.resolve_dig, 'DIGSCAN'),
|
||||
(resolver.resolve_mdns, 'AVAHISCAN'),
|
||||
(resolver.resolve_nslookup, 'NSLOOKUP'),
|
||||
(resolver.resolve_nbtlookup, 'NBTSCAN')
|
||||
(resolver.resolve_dig, "DIGSCAN"),
|
||||
(resolver.resolve_mdns, "AVAHISCAN"),
|
||||
(resolver.resolve_nslookup, "NSLOOKUP"),
|
||||
(resolver.resolve_nbtlookup, "NBTSCAN"),
|
||||
]
|
||||
|
||||
def resolve_devices(devices, resolve_both_name_and_fqdn=True):
|
||||
"""
|
||||
Attempts to resolve device names and/or FQDNs using available strategies.
|
||||
|
||||
|
||||
Parameters:
|
||||
devices (list): List of devices to resolve.
|
||||
resolve_both_name_and_fqdn (bool): If True, resolves both name and FQDN.
|
||||
If False, resolves only FQDN.
|
||||
|
||||
|
||||
Returns:
|
||||
recordsToUpdate (list): List of [newName, newFQDN, devMac] or [newFQDN, devMac] for DB update.
|
||||
recordsNotFound (list): List of [nameNotFound, devMac] for DB update.
|
||||
@@ -580,65 +666,93 @@ def update_devices_names(pm):
|
||||
|
||||
for device in devices:
|
||||
newName = nameNotFound
|
||||
newFQDN = ''
|
||||
newFQDN = ""
|
||||
|
||||
# Attempt each resolution strategy in order
|
||||
for resolve_fn, label in strategies:
|
||||
resolved = resolve_fn(device['devMac'], device['devLastIP'])
|
||||
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
|
||||
|
||||
# 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:
|
||||
if (
|
||||
newFQDN not in [nameNotFound, "", "localhost."]
|
||||
and " communications error to " not in newFQDN
|
||||
):
|
||||
foundStats[label] += 1
|
||||
|
||||
if resolve_both_name_and_fqdn:
|
||||
recordsToUpdate.append([newName, newFQDN, device['devMac']])
|
||||
recordsToUpdate.append([newName, newFQDN, device["devMac"]])
|
||||
else:
|
||||
recordsToUpdate.append([newFQDN, device['devMac']])
|
||||
recordsToUpdate.append([newFQDN, device["devMac"]])
|
||||
break
|
||||
|
||||
# If no name was resolved, queue device for "(name not found)" update
|
||||
if resolve_both_name_and_fqdn and newName == nameNotFound:
|
||||
notFound += 1
|
||||
if device['devName'] != nameNotFound:
|
||||
recordsNotFound.append([nameNotFound, device['devMac']])
|
||||
if device["devName"] != nameNotFound:
|
||||
recordsNotFound.append([nameNotFound, device["devMac"]])
|
||||
|
||||
return recordsToUpdate, recordsNotFound, foundStats, notFound
|
||||
|
||||
# --- Step 1: Update device names for unknown devices ---
|
||||
unknownDevices = device_handler.getUnknown()
|
||||
if unknownDevices:
|
||||
mylog('verbose', f'[Update Device Name] Trying to resolve devices without name. Unknown devices count: {len(unknownDevices)}')
|
||||
mylog(
|
||||
"verbose",
|
||||
f"[Update Device Name] Trying to resolve devices without name. Unknown devices count: {len(unknownDevices)}",
|
||||
)
|
||||
|
||||
# Try resolving both name and FQDN
|
||||
recordsToUpdate, recordsNotFound, foundStats, notFound = resolve_devices(unknownDevices)
|
||||
recordsToUpdate, recordsNotFound, foundStats, notFound = resolve_devices(
|
||||
unknownDevices
|
||||
)
|
||||
|
||||
# Log summary
|
||||
mylog('verbose', f"[Update Device Name] Names Found (DIGSCAN/AVAHISCAN/NSLOOKUP/NBTSCAN): {len(recordsToUpdate)} ({foundStats['DIGSCAN']}/{foundStats['AVAHISCAN']}/{foundStats['NSLOOKUP']}/{foundStats['NBTSCAN']})")
|
||||
mylog('verbose', f'[Update Device Name] Names Not Found : {notFound}')
|
||||
mylog(
|
||||
"verbose",
|
||||
f"[Update Device Name] Names Found (DIGSCAN/AVAHISCAN/NSLOOKUP/NBTSCAN): {len(recordsToUpdate)} ({foundStats['DIGSCAN']}/{foundStats['AVAHISCAN']}/{foundStats['NSLOOKUP']}/{foundStats['NBTSCAN']})",
|
||||
)
|
||||
mylog("verbose", f"[Update Device Name] Names Not Found : {notFound}")
|
||||
|
||||
# Apply updates to database
|
||||
sql.executemany("UPDATE Devices SET devName = ? WHERE devMac = ?", recordsNotFound)
|
||||
sql.executemany("UPDATE Devices SET devName = ?, devFQDN = ? WHERE devMac = ?", recordsToUpdate)
|
||||
sql.executemany(
|
||||
"UPDATE Devices SET devName = ? WHERE devMac = ?", recordsNotFound
|
||||
)
|
||||
sql.executemany(
|
||||
"UPDATE Devices SET devName = ?, devFQDN = ? WHERE devMac = ?",
|
||||
recordsToUpdate,
|
||||
)
|
||||
|
||||
# --- Step 2: Optionally refresh FQDN for all devices ---
|
||||
if get_setting_value("REFRESH_FQDN"):
|
||||
allDevices = device_handler.getAll()
|
||||
if allDevices:
|
||||
mylog('verbose', f'[Update FQDN] Trying to resolve FQDN. Devices count: {len(allDevices)}')
|
||||
mylog(
|
||||
"verbose",
|
||||
f"[Update FQDN] Trying to resolve FQDN. Devices count: {len(allDevices)}",
|
||||
)
|
||||
|
||||
# Try resolving only FQDN
|
||||
recordsToUpdate, _, foundStats, notFound = resolve_devices(allDevices, resolve_both_name_and_fqdn=False)
|
||||
recordsToUpdate, _, foundStats, notFound = resolve_devices(
|
||||
allDevices, resolve_both_name_and_fqdn=False
|
||||
)
|
||||
|
||||
# Log summary
|
||||
mylog('verbose', f"[Update FQDN] Names Found (DIGSCAN/AVAHISCAN/NSLOOKUP/NBTSCAN): {len(recordsToUpdate)} ({foundStats['DIGSCAN']}/{foundStats['AVAHISCAN']}/{foundStats['NSLOOKUP']}/{foundStats['NBTSCAN']})")
|
||||
mylog('verbose', f'[Update FQDN] Names Not Found : {notFound}')
|
||||
mylog(
|
||||
"verbose",
|
||||
f"[Update FQDN] Names Found (DIGSCAN/AVAHISCAN/NSLOOKUP/NBTSCAN): {len(recordsToUpdate)}"+
|
||||
f"({foundStats['DIGSCAN']}/{foundStats['AVAHISCAN']}/{foundStats['NSLOOKUP']}"+
|
||||
f"/{foundStats['NBTSCAN']})",
|
||||
)
|
||||
mylog("verbose", f"[Update FQDN] Names Not Found : {notFound}")
|
||||
|
||||
# Apply FQDN-only updates
|
||||
sql.executemany("UPDATE Devices SET devFQDN = ? WHERE devMac = ?", recordsToUpdate)
|
||||
sql.executemany(
|
||||
"UPDATE Devices SET devFQDN = ? WHERE devMac = ?", recordsToUpdate
|
||||
)
|
||||
|
||||
# Commit all database changes
|
||||
pm.db.commitDB()
|
||||
@@ -650,7 +764,8 @@ def update_devices_names(pm):
|
||||
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
|
||||
def update_devPresentLastScan_based_on_nics(db):
|
||||
"""
|
||||
@@ -707,65 +822,75 @@ def update_devPresentLastScan_based_on_nics(db):
|
||||
# Step 3: Execute batch update
|
||||
for present, mac in updates:
|
||||
sql.execute(
|
||||
"UPDATE Devices SET devPresentLastScan = ? WHERE devMac = ?",
|
||||
(present, mac)
|
||||
"UPDATE Devices SET devPresentLastScan = ? WHERE devMac = ?", (present, mac)
|
||||
)
|
||||
|
||||
db.commitDB()
|
||||
return len(updates)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
# Check if the variable contains a valid MAC address or "Internet"
|
||||
def check_mac_or_internet(input_str):
|
||||
# Regular expression pattern for matching a MAC address
|
||||
mac_pattern = r'([0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2})'
|
||||
mac_pattern = r"([0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2})"
|
||||
|
||||
if input_str.lower() == 'internet':
|
||||
if input_str.lower() == "internet":
|
||||
return True
|
||||
elif re.match(mac_pattern, input_str):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Lookup unknown vendors on devices
|
||||
def query_MAC_vendor (pMAC):
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
# Lookup unknown vendors on devices
|
||||
def query_MAC_vendor(pMAC):
|
||||
pMACstr = str(pMAC)
|
||||
|
||||
filePath = vendorsPath
|
||||
|
||||
|
||||
if os.path.isfile(vendorsPathNewest):
|
||||
filePath = vendorsPathNewest
|
||||
|
||||
|
||||
# Check MAC parameter
|
||||
mac = pMACstr.replace (':','').lower()
|
||||
if len(pMACstr) != 17 or len(mac) != 12 :
|
||||
return -2 # return -2 if ignored MAC
|
||||
mac = pMACstr.replace(":", "").lower()
|
||||
if len(pMACstr) != 17 or len(mac) != 12:
|
||||
return -2 # return -2 if ignored MAC
|
||||
|
||||
# Search vendor in HW Vendors DB
|
||||
mac_start_string6 = mac[0:6]
|
||||
mac_start_string9 = mac[0:9]
|
||||
mac_start_string6 = mac[0:6]
|
||||
mac_start_string9 = mac[0:9]
|
||||
|
||||
try:
|
||||
with open(filePath, 'r') as f:
|
||||
with open(filePath, "r") as f:
|
||||
for line in f:
|
||||
line_lower = line.lower() # Convert line to lowercase for case-insensitive matching
|
||||
if line_lower.startswith(mac_start_string6):
|
||||
parts = line.split('\t', 1)
|
||||
line_lower = (
|
||||
line.lower()
|
||||
) # Convert line to lowercase for case-insensitive matching
|
||||
if line_lower.startswith(mac_start_string6):
|
||||
parts = line.split("\t", 1)
|
||||
if len(parts) > 1:
|
||||
vendor = parts[1].strip()
|
||||
mylog('debug', [f"[Vendor Check] Found '{vendor}' for '{pMAC}' in {vendorsPath}"])
|
||||
mylog(
|
||||
"debug",
|
||||
[
|
||||
f"[Vendor Check] Found '{vendor}' for '{pMAC}' in {vendorsPath}"
|
||||
],
|
||||
)
|
||||
return vendor
|
||||
else:
|
||||
mylog('debug', [f'[Vendor Check] ⚠ ERROR: Match found, but line could not be processed: "{line_lower}"'])
|
||||
mylog(
|
||||
"debug",
|
||||
[
|
||||
f'[Vendor Check] ⚠ ERROR: Match found, but line could not be processed: "{line_lower}"'
|
||||
],
|
||||
)
|
||||
return -1
|
||||
|
||||
|
||||
return -1 # MAC address not found in the database
|
||||
except FileNotFoundError:
|
||||
mylog('none', [f"[Vendor Check] ⚠ ERROR: Vendors file {vendorsPath} not found."])
|
||||
mylog(
|
||||
"none", [f"[Vendor Check] ⚠ ERROR: Vendors file {vendorsPath} not found."]
|
||||
)
|
||||
return -1
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import json
|
||||
import base64
|
||||
from pathlib import Path
|
||||
from typing import Optional, List, Tuple, Dict
|
||||
from typing import Optional, Tuple
|
||||
|
||||
# Register NetAlertX directories
|
||||
INSTALL_PATH = "/app"
|
||||
INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
|
||||
sys.path.extend([f"{INSTALL_PATH}/server"])
|
||||
|
||||
import conf
|
||||
from const import *
|
||||
from logger import mylog
|
||||
from helper import timeNowTZ, get_setting_value
|
||||
|
||||
# Load MAC/device-type/icon rules from external file
|
||||
MAC_TYPE_ICON_PATH = Path(f"{INSTALL_PATH}/back/device_heuristics_rules.json")
|
||||
@@ -30,15 +28,16 @@ try:
|
||||
rule["icon_base64"] = ""
|
||||
except Exception as e:
|
||||
MAC_TYPE_ICON_RULES = []
|
||||
mylog('none', f"[guess_device_attributes] Failed to load device_heuristics_rules.json: {e}")
|
||||
mylog(
|
||||
"none",
|
||||
f"[guess_device_attributes] Failed to load device_heuristics_rules.json: {e}",
|
||||
)
|
||||
|
||||
|
||||
# -----------------------------------------
|
||||
# Match device type and base64-encoded icon using MAC prefix and vendor patterns.
|
||||
def match_mac_and_vendor(
|
||||
mac_clean: str,
|
||||
vendor: str,
|
||||
default_type: str,
|
||||
default_icon: str
|
||||
mac_clean: str, vendor: str, default_type: str, default_icon: str
|
||||
) -> Tuple[str, str]:
|
||||
"""
|
||||
Match device type and base64-encoded icon using MAC prefix and vendor patterns.
|
||||
@@ -63,8 +62,7 @@ def match_mac_and_vendor(
|
||||
|
||||
if mac_clean.startswith(mac_prefix):
|
||||
if not vendor_pattern or vendor_pattern in vendor:
|
||||
|
||||
mylog('debug', f"[guess_device_attributes] Matched via MAC+Vendor")
|
||||
mylog("debug", "[guess_device_attributes] Matched via MAC+Vendor")
|
||||
|
||||
type_ = dev_type
|
||||
icon = base64_icon or default_icon
|
||||
@@ -72,14 +70,10 @@ def match_mac_and_vendor(
|
||||
|
||||
return default_type, default_icon
|
||||
|
||||
|
||||
# ---------------------------------------------------
|
||||
# Match device type and base64-encoded icon using vendor patterns.
|
||||
def match_vendor(
|
||||
vendor: str,
|
||||
default_type: str,
|
||||
default_icon: str
|
||||
) -> Tuple[str, str]:
|
||||
|
||||
def match_vendor(vendor: str, default_type: str, default_icon: str) -> Tuple[str, str]:
|
||||
vendor_lc = vendor.lower()
|
||||
|
||||
for rule in MAC_TYPE_ICON_RULES:
|
||||
@@ -92,9 +86,8 @@ def match_vendor(
|
||||
mac_prefix = pattern.get("mac_prefix", "")
|
||||
vendor_pattern = pattern.get("vendor", "").lower()
|
||||
|
||||
if vendor_pattern and vendor_pattern in vendor_lc:
|
||||
|
||||
mylog('debug', f"[guess_device_attributes] Matched via Vendor")
|
||||
if vendor_pattern and vendor_pattern in vendor_lc:
|
||||
mylog("debug", "[guess_device_attributes] Matched via Vendor")
|
||||
|
||||
icon = base64_icon or default_icon
|
||||
|
||||
@@ -102,13 +95,10 @@ def match_vendor(
|
||||
|
||||
return default_type, default_icon
|
||||
|
||||
|
||||
# ---------------------------------------------------
|
||||
# Match device type and base64-encoded icon using name patterns.
|
||||
def match_name(
|
||||
name: str,
|
||||
default_type: str,
|
||||
default_icon: str
|
||||
) -> Tuple[str, str]:
|
||||
def match_name(name: str, default_type: str, default_icon: str) -> Tuple[str, str]:
|
||||
"""
|
||||
Match device type and base64-encoded icon using name patterns from global MAC_TYPE_ICON_RULES.
|
||||
|
||||
@@ -130,8 +120,7 @@ def match_name(
|
||||
for pattern in name_patterns:
|
||||
# Use regex search to allow pattern substrings
|
||||
if re.search(pattern, name_lower, re.IGNORECASE):
|
||||
|
||||
mylog('debug', f"[guess_device_attributes] Matched via Name")
|
||||
mylog("debug", "[guess_device_attributes] Matched via Name")
|
||||
|
||||
type_ = dev_type
|
||||
icon = base64_icon or default_icon
|
||||
@@ -139,13 +128,10 @@ def match_name(
|
||||
|
||||
return default_type, default_icon
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
#
|
||||
def match_ip(
|
||||
ip: str,
|
||||
default_type: str,
|
||||
default_icon: str
|
||||
) -> Tuple[str, str]:
|
||||
def match_ip(ip: str, default_type: str, default_icon: str) -> Tuple[str, str]:
|
||||
"""
|
||||
Match device type and base64-encoded icon using IP regex patterns from global JSON.
|
||||
|
||||
@@ -167,8 +153,7 @@ def match_ip(
|
||||
|
||||
for pattern in ip_patterns:
|
||||
if re.match(pattern, ip):
|
||||
|
||||
mylog('debug', f"[guess_device_attributes] Matched via IP")
|
||||
mylog("debug", "[guess_device_attributes] Matched via IP")
|
||||
|
||||
type_ = dev_type
|
||||
icon = base64_icon or default_icon
|
||||
@@ -176,7 +161,8 @@ def match_ip(
|
||||
|
||||
return default_type, default_icon
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
# Guess device attributes such as type of device and associated device icon
|
||||
def guess_device_attributes(
|
||||
vendor: Optional[str],
|
||||
@@ -184,16 +170,19 @@ def guess_device_attributes(
|
||||
ip: Optional[str],
|
||||
name: Optional[str],
|
||||
default_icon: str,
|
||||
default_type: str
|
||||
default_type: str,
|
||||
) -> Tuple[str, str]:
|
||||
mylog('debug', f"[guess_device_attributes] Guessing attributes for (vendor|mac|ip|name): ('{vendor}'|'{mac}'|'{ip}'|'{name}')")
|
||||
mylog(
|
||||
"debug",
|
||||
f"[guess_device_attributes] Guessing attributes for (vendor|mac|ip|name): ('{vendor}'|'{mac}'|'{ip}'|'{name}')",
|
||||
)
|
||||
|
||||
# --- Normalize inputs ---
|
||||
vendor = str(vendor).lower().strip() if vendor else "unknown"
|
||||
mac = str(mac).upper().strip() if mac else "00:00:00:00:00:00"
|
||||
ip = str(ip).strip() if ip else "169.254.0.0"
|
||||
name = str(name).lower().strip() if name else "(unknown)"
|
||||
mac_clean = mac.replace(':', '').replace('-', '').upper()
|
||||
mac_clean = mac.replace(":", "").replace("-", "").upper()
|
||||
|
||||
# # Internet shortcut
|
||||
# if mac == "INTERNET":
|
||||
@@ -221,7 +210,10 @@ def guess_device_attributes(
|
||||
type_ = type_ or default_type
|
||||
icon = icon or default_icon
|
||||
|
||||
mylog('debug', f"[guess_device_attributes] Guessed attributes (icon|type_): ('{icon}'|'{type_}')")
|
||||
mylog(
|
||||
"debug",
|
||||
f"[guess_device_attributes] Guessed attributes (icon|type_): ('{icon}'|'{type_}')",
|
||||
)
|
||||
return icon, type_
|
||||
|
||||
|
||||
@@ -231,8 +223,8 @@ def guess_icon(
|
||||
mac: Optional[str],
|
||||
ip: Optional[str],
|
||||
name: Optional[str],
|
||||
default: str
|
||||
) -> str:
|
||||
default: str,
|
||||
) -> str:
|
||||
"""
|
||||
[DEPRECATED] Guess the appropriate FontAwesome icon for a device based on its attributes.
|
||||
Use guess_device_attributes instead.
|
||||
@@ -247,17 +239,18 @@ def guess_icon(
|
||||
Returns:
|
||||
str: Base64-encoded FontAwesome icon HTML string.
|
||||
"""
|
||||
|
||||
|
||||
icon, _ = guess_device_attributes(vendor, mac, ip, name, default, "unknown_type")
|
||||
return icon
|
||||
|
||||
|
||||
def guess_type(
|
||||
vendor: Optional[str],
|
||||
mac: Optional[str],
|
||||
ip: Optional[str],
|
||||
name: Optional[str],
|
||||
default: str
|
||||
) -> str:
|
||||
default: str,
|
||||
) -> str:
|
||||
"""
|
||||
[DEPRECATED] Guess the device type based on its attributes.
|
||||
Use guess_device_attributes instead.
|
||||
@@ -272,11 +265,11 @@ def guess_type(
|
||||
Returns:
|
||||
str: Device type.
|
||||
"""
|
||||
|
||||
|
||||
_, type_ = guess_device_attributes(vendor, mac, ip, name, "unknown_icon", default)
|
||||
return type_
|
||||
|
||||
|
||||
# Handler for when this is run as a program instead of called as a module.
|
||||
if __name__ == "__main__":
|
||||
mylog('error', "This module is not intended to be run directly.")
|
||||
|
||||
mylog("error", "This module is not intended to be run directly.")
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import socket
|
||||
import dns.resolver
|
||||
|
||||
# Register NetAlertX directories
|
||||
INSTALL_PATH = "/app"
|
||||
INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
|
||||
sys.path.extend([f"{INSTALL_PATH}/server"])
|
||||
|
||||
import conf
|
||||
from const import *
|
||||
from logger import mylog
|
||||
from helper import get_setting_value
|
||||
|
||||
|
||||
class ResolvedName:
|
||||
def __init__(self, raw: str = "(name not found)", cleaned: str = "(name not found)"):
|
||||
def __init__(
|
||||
self, raw: str = "(name not found)", cleaned: str = "(name not found)"
|
||||
):
|
||||
self.raw = raw
|
||||
self.cleaned = cleaned
|
||||
|
||||
def __str__(self):
|
||||
return self.cleaned
|
||||
|
||||
|
||||
class NameResolver:
|
||||
def __init__(self, db):
|
||||
self.db = db
|
||||
@@ -66,18 +66,18 @@ class NameResolver:
|
||||
return self.resolve_from_plugin("DIGSCAN", pMAC, pIP)
|
||||
|
||||
def clean_device_name(self, name: str, match_ip: bool) -> str:
|
||||
mylog('debug', [f"[cleanDeviceName] input: {name}"])
|
||||
mylog("debug", [f"[cleanDeviceName] input: {name}"])
|
||||
|
||||
if match_ip:
|
||||
name += " (IP match)"
|
||||
|
||||
regexes = get_setting_value('NEWDEV_NAME_CLEANUP_REGEX') or []
|
||||
regexes = get_setting_value("NEWDEV_NAME_CLEANUP_REGEX") or []
|
||||
for rgx in regexes:
|
||||
mylog('trace', [f"[cleanDeviceName] applying regex: {rgx}"])
|
||||
mylog("trace", [f"[cleanDeviceName] applying regex: {rgx}"])
|
||||
name = re.sub(rgx, "", name)
|
||||
|
||||
name = re.sub(r'\.$', '', name)
|
||||
name = re.sub(r"\.$", "", name)
|
||||
name = name.replace(". (IP match)", " (IP match)")
|
||||
|
||||
mylog('debug', [f"[cleanDeviceName] output: {name}"])
|
||||
mylog("debug", [f"[cleanDeviceName] output: {name}"])
|
||||
return name
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Register NetAlertX directories
|
||||
INSTALL_PATH="/app"
|
||||
INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
|
||||
sys.path.extend([f"{INSTALL_PATH}/server"])
|
||||
|
||||
import conf
|
||||
from scan.device_handling import create_new_devices, print_scan_stats, save_scanned_devices, exclude_ignored_devices, update_devices_data_from_scan
|
||||
from scan.device_handling import (
|
||||
create_new_devices,
|
||||
print_scan_stats,
|
||||
save_scanned_devices,
|
||||
exclude_ignored_devices,
|
||||
update_devices_data_from_scan,
|
||||
)
|
||||
from helper import timeNowTZ, get_setting_value
|
||||
from db.db_helper import print_table_schema
|
||||
from logger import mylog, Logger
|
||||
@@ -13,73 +19,75 @@ from messaging.reporting import skip_repeated_notifications
|
||||
|
||||
|
||||
# Make sure log level is initialized correctly
|
||||
Logger(get_setting_value('LOG_LEVEL'))
|
||||
Logger(get_setting_value("LOG_LEVEL"))
|
||||
|
||||
#===============================================================================
|
||||
# ===============================================================================
|
||||
# SCAN NETWORK
|
||||
#===============================================================================
|
||||
# ===============================================================================
|
||||
|
||||
def process_scan (db):
|
||||
|
||||
def process_scan(db):
|
||||
# Apply exclusions
|
||||
mylog('verbose','[Process Scan] Exclude ignored devices')
|
||||
exclude_ignored_devices (db)
|
||||
mylog("verbose", "[Process Scan] Exclude ignored devices")
|
||||
exclude_ignored_devices(db)
|
||||
|
||||
# Load current scan data
|
||||
mylog('verbose','[Process Scan] Processing scan results')
|
||||
save_scanned_devices (db)
|
||||
mylog("verbose", "[Process Scan] Processing scan results")
|
||||
save_scanned_devices(db)
|
||||
|
||||
db.commitDB()
|
||||
|
||||
# Print stats
|
||||
mylog('none','[Process Scan] Print Stats')
|
||||
print_scan_stats(db)
|
||||
mylog('none','[Process Scan] Stats end')
|
||||
|
||||
# Create Events
|
||||
mylog('verbose','[Process Scan] Sessions Events (connect / disconnect)')
|
||||
# Print stats
|
||||
mylog("none", "[Process Scan] Print Stats")
|
||||
print_scan_stats(db)
|
||||
mylog("none", "[Process Scan] Stats end")
|
||||
|
||||
# Create Events
|
||||
mylog("verbose", "[Process Scan] Sessions Events (connect / disconnect)")
|
||||
insert_events(db)
|
||||
|
||||
# Create New Devices
|
||||
# after create events -> avoid 'connection' event
|
||||
mylog('verbose','[Process Scan] Creating new devices')
|
||||
create_new_devices (db)
|
||||
mylog("verbose", "[Process Scan] Creating new devices")
|
||||
create_new_devices(db)
|
||||
|
||||
# Update devices info
|
||||
mylog('verbose','[Process Scan] Updating Devices Info')
|
||||
update_devices_data_from_scan (db)
|
||||
mylog("verbose", "[Process Scan] Updating Devices Info")
|
||||
update_devices_data_from_scan(db)
|
||||
|
||||
# Pair session events (Connection / Disconnection)
|
||||
mylog('verbose','[Process Scan] Pairing session events (connection / disconnection) ')
|
||||
pair_sessions_events(db)
|
||||
|
||||
# Sessions snapshot
|
||||
mylog('verbose','[Process Scan] Creating sessions snapshot')
|
||||
create_sessions_snapshot (db)
|
||||
mylog(
|
||||
"verbose", "[Process Scan] Pairing session events (connection / disconnection) "
|
||||
)
|
||||
pair_sessions_events(db)
|
||||
|
||||
# Sessions snapshot
|
||||
mylog('verbose','[Process Scan] Inserting scan results into Online_History')
|
||||
mylog("verbose", "[Process Scan] Creating sessions snapshot")
|
||||
create_sessions_snapshot(db)
|
||||
|
||||
# Sessions snapshot
|
||||
mylog("verbose", "[Process Scan] Inserting scan results into Online_History")
|
||||
insertOnlineHistory(db)
|
||||
|
||||
# Skip repeated notifications
|
||||
mylog('verbose','[Process Scan] Skipping repeated notifications')
|
||||
skip_repeated_notifications (db)
|
||||
|
||||
# Clear current scan as processed
|
||||
# Skip repeated notifications
|
||||
mylog("verbose", "[Process Scan] Skipping repeated notifications")
|
||||
skip_repeated_notifications(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")
|
||||
|
||||
# Commit changes
|
||||
db.sql.execute("DELETE FROM CurrentScan")
|
||||
|
||||
# Commit changes
|
||||
db.commitDB()
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def pair_sessions_events (db):
|
||||
|
||||
sql = db.sql #TO-DO
|
||||
# -------------------------------------------------------------------------------
|
||||
def pair_sessions_events(db):
|
||||
sql = db.sql # TO-DO
|
||||
# Pair Connection / New Device events
|
||||
|
||||
mylog('debug','[Pair Session] - 1 Connections / New Devices')
|
||||
sql.execute ("""UPDATE Events
|
||||
mylog("debug", "[Pair Session] - 1 Connections / New Devices")
|
||||
sql.execute("""UPDATE Events
|
||||
SET eve_PairEventRowid =
|
||||
(SELECT ROWID
|
||||
FROM Events AS EVE2
|
||||
@@ -90,49 +98,48 @@ def pair_sessions_events (db):
|
||||
ORDER BY EVE2.eve_DateTime ASC LIMIT 1)
|
||||
WHERE eve_EventType IN ('New Device', 'Connected', 'Down Reconnected')
|
||||
AND eve_PairEventRowid IS NULL
|
||||
""" )
|
||||
""")
|
||||
|
||||
# Pair Disconnection / Device Down
|
||||
mylog('debug','[Pair Session] - 2 Disconnections')
|
||||
sql.execute ("""UPDATE Events
|
||||
mylog("debug", "[Pair Session] - 2 Disconnections")
|
||||
sql.execute("""UPDATE Events
|
||||
SET eve_PairEventRowid =
|
||||
(SELECT ROWID
|
||||
FROM Events AS EVE2
|
||||
WHERE EVE2.eve_PairEventRowid = Events.ROWID)
|
||||
WHERE eve_EventType IN ('Device Down', 'Disconnected')
|
||||
AND eve_PairEventRowid IS NULL
|
||||
""" )
|
||||
""")
|
||||
|
||||
|
||||
mylog('debug','[Pair Session] Pair session end')
|
||||
mylog("debug", "[Pair Session] Pair session end")
|
||||
db.commitDB()
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def create_sessions_snapshot (db):
|
||||
sql = db.sql #TO-DO
|
||||
# -------------------------------------------------------------------------------
|
||||
def create_sessions_snapshot(db):
|
||||
sql = db.sql # TO-DO
|
||||
|
||||
# Clean sessions snapshot
|
||||
mylog('debug','[Sessions Snapshot] - 1 Clean')
|
||||
sql.execute ("DELETE FROM SESSIONS" )
|
||||
mylog("debug", "[Sessions Snapshot] - 1 Clean")
|
||||
sql.execute("DELETE FROM SESSIONS")
|
||||
|
||||
# Insert sessions
|
||||
mylog('debug','[Sessions Snapshot] - 2 Insert')
|
||||
sql.execute ("""INSERT INTO Sessions
|
||||
SELECT * FROM Convert_Events_to_Sessions""" )
|
||||
mylog("debug", "[Sessions Snapshot] - 2 Insert")
|
||||
sql.execute("""INSERT INTO Sessions
|
||||
SELECT * FROM Convert_Events_to_Sessions""")
|
||||
|
||||
mylog('debug','[Sessions Snapshot] Sessions end')
|
||||
mylog("debug", "[Sessions Snapshot] Sessions end")
|
||||
db.commitDB()
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def insert_events (db):
|
||||
sql = db.sql #TO-DO
|
||||
startTime = timeNowTZ()
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
def insert_events(db):
|
||||
sql = db.sql # TO-DO
|
||||
startTime = timeNowTZ()
|
||||
|
||||
# Check device down
|
||||
mylog('debug','[Events] - 1 - Devices down')
|
||||
sql.execute (f"""INSERT INTO Events (eve_MAC, eve_IP, eve_DateTime,
|
||||
mylog("debug", "[Events] - 1 - Devices down")
|
||||
sql.execute(f"""INSERT INTO Events (eve_MAC, eve_IP, eve_DateTime,
|
||||
eve_EventType, eve_AdditionalInfo,
|
||||
eve_PendingAlertEmail)
|
||||
SELECT devMac, devLastIP, '{startTime}', 'Device Down', '', 1
|
||||
@@ -144,8 +151,8 @@ def insert_events (db):
|
||||
) """)
|
||||
|
||||
# Check new Connections or Down Reconnections
|
||||
mylog('debug','[Events] - 2 - New Connections')
|
||||
sql.execute (f""" INSERT INTO Events (eve_MAC, eve_IP, eve_DateTime,
|
||||
mylog("debug", "[Events] - 2 - New Connections")
|
||||
sql.execute(f""" INSERT INTO Events (eve_MAC, eve_IP, eve_DateTime,
|
||||
eve_EventType, eve_AdditionalInfo,
|
||||
eve_PendingAlertEmail)
|
||||
SELECT DISTINCT c.cur_MAC, c.cur_IP, '{startTime}',
|
||||
@@ -161,8 +168,8 @@ def insert_events (db):
|
||||
""")
|
||||
|
||||
# Check disconnections
|
||||
mylog('debug','[Events] - 3 - Disconnections')
|
||||
sql.execute (f"""INSERT INTO Events (eve_MAC, eve_IP, eve_DateTime,
|
||||
mylog("debug", "[Events] - 3 - Disconnections")
|
||||
sql.execute(f"""INSERT INTO Events (eve_MAC, eve_IP, eve_DateTime,
|
||||
eve_EventType, eve_AdditionalInfo,
|
||||
eve_PendingAlertEmail)
|
||||
SELECT devMac, devLastIP, '{startTime}', 'Disconnected', '',
|
||||
@@ -175,19 +182,19 @@ def insert_events (db):
|
||||
) """)
|
||||
|
||||
# Check IP Changed
|
||||
mylog('debug','[Events] - 4 - IP Changes')
|
||||
sql.execute (f"""INSERT INTO Events (eve_MAC, eve_IP, eve_DateTime,
|
||||
mylog("debug", "[Events] - 4 - IP Changes")
|
||||
sql.execute(f"""INSERT INTO Events (eve_MAC, eve_IP, eve_DateTime,
|
||||
eve_EventType, eve_AdditionalInfo,
|
||||
eve_PendingAlertEmail)
|
||||
SELECT cur_MAC, cur_IP, '{startTime}', 'IP Changed',
|
||||
'Previous IP: '|| devLastIP, devAlertEvents
|
||||
FROM Devices, CurrentScan
|
||||
WHERE devMac = cur_MAC
|
||||
AND devLastIP <> cur_IP """ )
|
||||
mylog('debug','[Events] - Events end')
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
AND devLastIP <> cur_IP """)
|
||||
mylog("debug", "[Events] - Events end")
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
def insertOnlineHistory(db):
|
||||
sql = db.sql # TO-DO: Implement sql object
|
||||
|
||||
@@ -202,14 +209,16 @@ def insertOnlineHistory(db):
|
||||
COALESCE(SUM(CASE WHEN devPresentLastScan = 0 AND devAlertDown = 1 THEN 1 ELSE 0 END), 0) AS downDevices
|
||||
FROM Devices
|
||||
"""
|
||||
|
||||
deviceCounts = db.read(query)[0] # Assuming db.read returns a list of rows, take the first (and only) row
|
||||
|
||||
allDevices = deviceCounts['allDevices']
|
||||
archivedDevices = deviceCounts['archivedDevices']
|
||||
onlineDevices = deviceCounts['onlineDevices']
|
||||
downDevices = deviceCounts['downDevices']
|
||||
|
||||
deviceCounts = db.read(query)[
|
||||
0
|
||||
] # Assuming db.read returns a list of rows, take the first (and only) row
|
||||
|
||||
allDevices = deviceCounts["allDevices"]
|
||||
archivedDevices = deviceCounts["archivedDevices"]
|
||||
onlineDevices = deviceCounts["onlineDevices"]
|
||||
downDevices = deviceCounts["downDevices"]
|
||||
|
||||
offlineDevices = allDevices - archivedDevices - onlineDevices
|
||||
|
||||
# Prepare the insert query using parameterized inputs
|
||||
@@ -217,15 +226,26 @@ def insertOnlineHistory(db):
|
||||
INSERT INTO Online_History (Scan_Date, Online_Devices, Down_Devices, All_Devices, Archived_Devices, Offline_Devices)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
"""
|
||||
|
||||
mylog('debug', f'[Presence graph] Sql query: {insert_query} with values: {scanTimestamp}, {onlineDevices}, {downDevices}, {allDevices}, {archivedDevices}, {offlineDevices}')
|
||||
|
||||
# Debug output
|
||||
mylog(
|
||||
"debug",
|
||||
f"[Presence graph] Sql query: {insert_query} with values: {scanTimestamp}, {onlineDevices}, {downDevices}, {allDevices}, {archivedDevices}, {offlineDevices}",
|
||||
)
|
||||
|
||||
# Debug output
|
||||
print_table_schema(db, "Online_History")
|
||||
|
||||
# Insert the gathered data into the history table
|
||||
sql.execute(insert_query, (scanTimestamp, onlineDevices, downDevices, allDevices, archivedDevices, offlineDevices))
|
||||
sql.execute(
|
||||
insert_query,
|
||||
(
|
||||
scanTimestamp,
|
||||
onlineDevices,
|
||||
downDevices,
|
||||
allDevices,
|
||||
archivedDevices,
|
||||
offlineDevices,
|
||||
),
|
||||
)
|
||||
|
||||
db.commitDB()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user