Merge remote-tracking branch 'origin/terorero-merge' into pr/74
This commit is contained in:
205
back/pialert.py
205
back/pialert.py
@@ -75,6 +75,9 @@ def main ():
|
||||
return
|
||||
cycle = str(sys.argv[1])
|
||||
|
||||
## Upgrade DB if needed
|
||||
upgradeDB()
|
||||
|
||||
## Main Commands
|
||||
if cycle == 'internet_IP':
|
||||
res = check_internet_IP()
|
||||
@@ -256,28 +259,35 @@ def check_IP_format (pIP):
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# INTERNET IP CHANGE
|
||||
# Cleanup Online History chart
|
||||
#===============================================================================
|
||||
def cleanup_database ():
|
||||
# Header
|
||||
print ('Cleanup Database')
|
||||
print (' Timestamp:', startTime )
|
||||
|
||||
openDB()
|
||||
strdaystokeepOH = str(DAYS_TO_KEEP_ONLINEHISTORY)
|
||||
strdaystokeepEV = str(DAYS_TO_KEEP_EVENTS)
|
||||
openDB()
|
||||
|
||||
# keep 10 years if not specified how many days to keep
|
||||
try:
|
||||
strdaystokeepEV = str(DAYS_TO_KEEP_EVENTS)
|
||||
except NameError: # variable not defined, use a default
|
||||
strdaystokeepEV = str(3650) # 10 years
|
||||
|
||||
# Cleanup Online History
|
||||
print ('\nCleanup Online_History, up to the lastest '+strdaystokeepOH+' days...')
|
||||
sql.execute ("DELETE FROM Online_History WHERE Scan_Date <= date('now', '-"+strdaystokeepOH+" day')")
|
||||
print ('\nCleanup Online_History...')
|
||||
sql.execute ("DELETE FROM Online_History WHERE Scan_Date <= date('now', '-1 day')")
|
||||
print ('\nOptimize Database...')
|
||||
|
||||
# Cleanup Events
|
||||
print ('\nCleanup Events, up to the lastest '+strdaystokeepEV+' days...')
|
||||
sql.execute ("DELETE FROM Events WHERE eve_DateTime <= date('now', '-"+strdaystokeepEV+" day')")
|
||||
|
||||
# Shrink DB
|
||||
print ('\nShrink Database...')
|
||||
sql.execute ("VACUUM;")
|
||||
|
||||
closeDB()
|
||||
|
||||
# OK
|
||||
return 0
|
||||
|
||||
@@ -382,15 +392,12 @@ def scan_network ():
|
||||
|
||||
# ScanCycle data
|
||||
cycle_interval = scanCycle_data['cic_EveryXmin']
|
||||
arpscan_retries = scanCycle_data['cic_arpscanCycles']
|
||||
# TESTING - Fast scan
|
||||
# arpscan_retries = 1
|
||||
|
||||
# arp-scan command
|
||||
print ('\nScanning...')
|
||||
print (' arp-scan Method...')
|
||||
print_log ('arp-scan starts...')
|
||||
arpscan_devices = execute_arpscan (arpscan_retries)
|
||||
arpscan_devices = execute_arpscan ()
|
||||
print_log ('arp-scan ends')
|
||||
# DEBUG - print number of rows updated
|
||||
# print (arpscan_devices)
|
||||
@@ -476,27 +483,13 @@ def query_ScanCycle_Data (pOpenCloseDB = False):
|
||||
return sqlRow
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def execute_arpscan (pRetries):
|
||||
|
||||
def execute_arpscan ():
|
||||
# #101 - arp-scan subnet configuration
|
||||
# Prepare command arguments
|
||||
# subnets = SCAN_SUBNETS.strip().split()
|
||||
|
||||
# arp-scan for larger Networks like /16
|
||||
# otherwise the system starts multiple processes. the 15min cronjob isn't necessary.
|
||||
# the scan is about 4min on a /16 network
|
||||
arpscan_args = ['sudo', 'arp-scan', '--ignoredups', '--bandwidth=512k', '--retry=3', '--localnet']
|
||||
|
||||
# Default arp-scan
|
||||
# arpscan_args = ['sudo', 'arp-scan', '--localnet', '--ignoredups', '--retry=' + str(pRetries)]
|
||||
# print (arpscan_args)
|
||||
|
||||
# TESTING - Fast Scan
|
||||
# arpscan_args = ['sudo', 'arp-scan', '--localnet', '--ignoredups', '--retry=1']
|
||||
|
||||
# DEBUG - arp-scan command
|
||||
# print (" ".join (arpscan_args))
|
||||
|
||||
subnets = SCAN_SUBNETS.strip().split()
|
||||
# Retry is 6 to avoid false offline devices
|
||||
arpscan_args = ['sudo', 'arp-scan', '--ignoredups', '--retry=6'] + subnets
|
||||
|
||||
# Execute command
|
||||
arpscan_output = subprocess.check_output (arpscan_args, universal_newlines=True)
|
||||
|
||||
@@ -510,9 +503,6 @@ def execute_arpscan (pRetries):
|
||||
devices_list = [device.groupdict()
|
||||
for device in re.finditer (re_pattern, arpscan_output)]
|
||||
|
||||
# Bugfix #5 - Delete duplicated MAC's with different IP's
|
||||
# TEST - Force duplicated device
|
||||
# devices_list.append(devices_list[0])
|
||||
# Delete duplicate MAC
|
||||
unique_mac = []
|
||||
unique_devices = []
|
||||
@@ -730,14 +720,17 @@ def print_scan_stats ():
|
||||
sql.execute("SELECT * FROM Devices")
|
||||
History_All = sql.fetchall()
|
||||
History_All_Devices = len(History_All)
|
||||
|
||||
sql.execute("SELECT * FROM Devices WHERE dev_Archived = 1")
|
||||
History_Archived = sql.fetchall()
|
||||
History_Archived_Devices = len(History_Archived)
|
||||
sql.execute("SELECT * FROM CurrentScan")
|
||||
|
||||
sql.execute("""SELECT * FROM CurrentScan WHERE cur_ScanCycle = ? """, (cycle,))
|
||||
History_Online = sql.fetchall()
|
||||
History_Online_Devices = len(History_Online)
|
||||
History_Offline_Devices = History_All_Devices - History_Archived_Devices - History_Online_Devices
|
||||
sql.execute ("INSERT INTO Online_History (Scan_Date, Online_Devices, Down_Devices, All_Devices, Archived_Devices ) "+
|
||||
|
||||
sql.execute ("INSERT INTO Online_History (Scan_Date, Online_Devices, Down_Devices, All_Devices, Archived_Devices) "+
|
||||
"VALUES ( ?, ?, ?, ?, ?)", (startTime, History_Online_Devices, History_Offline_Devices, History_All_Devices, History_Archived_Devices ) )
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
@@ -754,6 +747,16 @@ def create_new_devices ():
|
||||
WHERE dev_MAC = cur_MAC) """,
|
||||
(startTime, cycle) )
|
||||
|
||||
print_log ('New devices - Insert Connection into session table')
|
||||
sql.execute ("""INSERT INTO Sessions (ses_MAC, ses_IP, ses_EventTypeConnection, ses_DateTimeConnection,
|
||||
ses_EventTypeDisconnection, ses_DateTimeDisconnection, ses_StillConnected, ses_AdditionalInfo)
|
||||
SELECT cur_MAC, cur_IP,'Connected',?, NULL , NULL ,1, cur_Vendor
|
||||
FROM CurrentScan
|
||||
WHERE cur_ScanCycle = ?
|
||||
AND NOT EXISTS (SELECT 1 FROM Sessions
|
||||
WHERE ses_MAC = cur_MAC) """,
|
||||
(startTime, cycle) )
|
||||
|
||||
# arpscan - Create new devices
|
||||
print_log ('New devices - 2 Create devices')
|
||||
sql.execute ("""INSERT INTO Devices (dev_MAC, dev_name, dev_Vendor,
|
||||
@@ -981,17 +984,10 @@ def update_devices_data_from_scan ():
|
||||
sql.executemany ("UPDATE Devices SET dev_Vendor = ? WHERE dev_MAC = ? ",
|
||||
recordsToUpdate )
|
||||
|
||||
# New Apple devices -> Cycle 15
|
||||
print_log ('Update devices - 6 Cycle for Apple devices')
|
||||
sql.execute ("""UPDATE Devices SET dev_ScanCycle = 1
|
||||
WHERE dev_FirstConnection = ?
|
||||
AND UPPER(dev_Vendor) LIKE '%APPLE%' """,
|
||||
(startTime,) )
|
||||
|
||||
print_log ('Update devices end')
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Feature #43 - Resoltion name for unknown devices
|
||||
# Feature #43 - Resolve name for unknown devices
|
||||
def update_devices_names ():
|
||||
# Initialize variables
|
||||
recordsToUpdate = []
|
||||
@@ -1214,20 +1210,21 @@ def skip_repeated_notifications ():
|
||||
def email_reporting ():
|
||||
global mail_text
|
||||
global mail_html
|
||||
|
||||
# Reporting section
|
||||
print ('\nReporting...')
|
||||
openDB()
|
||||
|
||||
# Disable reporting on events for devices where reporting is disabled based on the MAC address
|
||||
sql.execute ("""UPDATE Events SET eve_PendingAlertEmail = 0
|
||||
WHERE eve_PendingAlertEmail = 1 AND eve_MAC IN
|
||||
WHERE eve_PendingAlertEmail = 1 AND eve_EventType != 'Device Down' AND eve_MAC IN
|
||||
(
|
||||
SELECT dev_MAC FROM Devices WHERE dev_AlertEvents = 0
|
||||
SELECT dev_MAC FROM Devices WHERE dev_AlertEvents = 0
|
||||
)""")
|
||||
sql.execute ("""UPDATE Events SET eve_PendingAlertEmail = 0
|
||||
WHERE eve_PendingAlertEmail = 1 AND eve_EventType = 'Device Down' AND eve_MAC IN
|
||||
(
|
||||
SELECT dev_MAC FROM Devices WHERE dev_AlertDeviceDown = 0
|
||||
)""")
|
||||
|
||||
# Open text Template
|
||||
|
||||
|
||||
# Open text Template
|
||||
template_file = open(PIALERT_BACK_PATH + '/report_template.txt', 'r')
|
||||
@@ -1274,16 +1271,18 @@ def email_reporting ():
|
||||
WHERE eve_PendingAlertEmail = 1 AND eve_MAC = 'Internet'
|
||||
ORDER BY eve_DateTime""")
|
||||
|
||||
|
||||
for eventAlert in sql :
|
||||
mail_section_Internet = True
|
||||
mail_text_Internet += text_line_template.format (
|
||||
eventAlert['eve_EventType'], eventAlert['eve_DateTime'],
|
||||
eventAlert['eve_IP'], eventAlert['eve_AdditionalInfo'])
|
||||
'Event:', eventAlert['eve_EventType'], 'Time:', eventAlert['eve_DateTime'],
|
||||
'IP:', eventAlert['eve_IP'], 'More Info:', eventAlert['eve_AdditionalInfo'])
|
||||
mail_html_Internet += html_line_template.format (
|
||||
REPORT_DEVICE_URL, eventAlert['eve_MAC'],
|
||||
eventAlert['eve_EventType'], eventAlert['eve_DateTime'],
|
||||
eventAlert['eve_IP'], eventAlert['eve_AdditionalInfo'])
|
||||
|
||||
|
||||
format_report_section (mail_section_Internet, 'SECTION_INTERNET',
|
||||
'TABLE_INTERNET', mail_text_Internet, mail_html_Internet)
|
||||
|
||||
@@ -1310,7 +1309,7 @@ def email_reporting ():
|
||||
REPORT_DEVICE_URL, eventAlert['eve_MAC'], eventAlert['eve_MAC'],
|
||||
eventAlert['eve_DateTime'], eventAlert['eve_IP'],
|
||||
eventAlert['dev_Name'], eventAlert['eve_AdditionalInfo'])
|
||||
|
||||
|
||||
format_report_section (mail_section_new_devices, 'SECTION_NEW_DEVICES',
|
||||
'TABLE_NEW_DEVICES', mail_text_new_devices, mail_html_new_devices)
|
||||
|
||||
@@ -1385,16 +1384,16 @@ def email_reporting ():
|
||||
send_email (mail_text, mail_html)
|
||||
else :
|
||||
print (' Skip mail...')
|
||||
if REPORT_PUSHSAFER :
|
||||
print (' Sending report by PUSHSAFER...')
|
||||
send_pushsafer (mail_text)
|
||||
else :
|
||||
print (' Skip PUSHSAFER...')
|
||||
if REPORT_NTFY :
|
||||
print (' Sending report by NTFY...')
|
||||
send_ntfy (mail_text)
|
||||
else :
|
||||
print (' Skip NTFY...')
|
||||
if REPORT_PUSHSAFER :
|
||||
print (' Sending report by PUSHSAFER...')
|
||||
send_pushsafer (mail_text)
|
||||
else :
|
||||
print (' Skip PUSHSAFER...')
|
||||
else :
|
||||
print (' No changes to report...')
|
||||
|
||||
@@ -1414,8 +1413,16 @@ def email_reporting ():
|
||||
# Commit changes
|
||||
sql_connection.commit()
|
||||
closeDB()
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def send_ntfy (_Text):
|
||||
requests.post("https://ntfy.sh/{}".format(NTFY_TOPIC),
|
||||
data=_Text,
|
||||
headers={
|
||||
"Title": "Pi.Alert Notification",
|
||||
"Actions": "view, Open Dashboard, "+ REPORT_DASHBOARD_URL,
|
||||
"Priority": "urgent",
|
||||
"Tags": "warning"
|
||||
})
|
||||
|
||||
def send_pushsafer (_Text):
|
||||
url = 'https://www.pushsafer.com/api'
|
||||
@@ -1431,24 +1438,8 @@ def send_pushsafer (_Text):
|
||||
"ut" : 'Open Pi.Alert',
|
||||
"k" : PUSHSAFER_TOKEN,
|
||||
}
|
||||
|
||||
requests.post(url, data=post_fields)
|
||||
#request = Request(url, urlencode(post_fields).encode())
|
||||
#json = urlopen(request).read().decode()
|
||||
# print(json)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
def send_ntfy (_Text):
|
||||
requests.post("https://ntfy.sh/{}".format(NTFY_TOPIC),
|
||||
data=_Text,
|
||||
headers={
|
||||
"Title": "Pi.Alert Notification",
|
||||
"Click": REPORT_DASHBOARD_URL,
|
||||
"Priority": "urgent",
|
||||
"Tags": "warning"
|
||||
})
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def format_report_section (pActive, pSection, pTable, pText, pHTML):
|
||||
global mail_text
|
||||
@@ -1539,6 +1530,69 @@ def SafeParseGlobalBool(boolVariable):
|
||||
#===============================================================================
|
||||
# DB
|
||||
#===============================================================================
|
||||
def upgradeDB ():
|
||||
|
||||
openDB()
|
||||
|
||||
# indicates, if Online_History table is available
|
||||
onlineHistoryAvailable = sql.execute("""
|
||||
SELECT name FROM sqlite_master WHERE type='table'
|
||||
AND name='Online_History';
|
||||
""").fetchall() != []
|
||||
|
||||
# Check if it is incompatible (Check if table has all required columns)
|
||||
isIncompatible = False
|
||||
|
||||
if onlineHistoryAvailable :
|
||||
isIncompatible = sql.execute ("""
|
||||
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Online_History') WHERE name='Archived_Devices'
|
||||
""").fetchone()[0] == 0
|
||||
|
||||
# Drop table if available, but incompatible
|
||||
if onlineHistoryAvailable and isIncompatible:
|
||||
print_log ('Table is incompatible, Dropping the Online_History table)')
|
||||
sql.execute("DROP TABLE Online_History;")
|
||||
onlineHistoryAvailable = False
|
||||
|
||||
if onlineHistoryAvailable == False :
|
||||
sql.execute("""
|
||||
CREATE TABLE "Online_History" (
|
||||
"Index" INTEGER,
|
||||
"Scan_Date" TEXT,
|
||||
"Online_Devices" INTEGER,
|
||||
"Down_Devices" INTEGER,
|
||||
"All_Devices" INTEGER,
|
||||
"Archived_Devices" INTEGER,
|
||||
PRIMARY KEY("Index" AUTOINCREMENT)
|
||||
);
|
||||
""")
|
||||
|
||||
# Alter Devices table
|
||||
# dev_Network_Node_MAC_ADDR column
|
||||
dev_Network_Node_MAC_ADDR_missing = sql.execute ("""
|
||||
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='dev_Network_Node_MAC_ADDR'
|
||||
""").fetchone()[0] == 0
|
||||
|
||||
if dev_Network_Node_MAC_ADDR_missing :
|
||||
sql.execute("""
|
||||
ALTER TABLE "Devices" ADD "dev_Network_Node_MAC_ADDR" TEXT
|
||||
""")
|
||||
|
||||
# dev_Network_Node_port column
|
||||
dev_Network_Node_port_missing = sql.execute ("""
|
||||
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='dev_Network_Node_port'
|
||||
""").fetchone()[0] == 0
|
||||
|
||||
if dev_Network_Node_port_missing :
|
||||
sql.execute("""
|
||||
ALTER TABLE "Devices" ADD "dev_Network_Node_port" INTEGER
|
||||
""")
|
||||
|
||||
# don't hog DB access
|
||||
closeDB ()
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
def openDB ():
|
||||
global sql_connection
|
||||
global sql
|
||||
@@ -1552,6 +1606,7 @@ def openDB ():
|
||||
|
||||
# Open DB and Cursor
|
||||
sql_connection = sqlite3.connect (DB_PATH, isolation_level=None)
|
||||
sql_connection.execute('pragma journal_mode=wal') #
|
||||
sql_connection.text_factory = str
|
||||
sql_connection.row_factory = sqlite3.Row
|
||||
sql = sql_connection.cursor()
|
||||
|
||||
Reference in New Issue
Block a user