/data and /tmp standarization

This commit is contained in:
Adam Outler
2025-11-04 22:26:35 +00:00
parent 90a07c61eb
commit 5b871865db
250 changed files with 7462 additions and 4940 deletions

View File

@@ -1,26 +1,26 @@
import sys
import sqlite3
import os
import sys
# Register NetAlertX directories
INSTALL_PATH="/app"
INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
sys.path.extend([f"{INSTALL_PATH}/server"])
import conf
from logger import mylog, Logger
from helper import get_setting_value, timeNowTZ
from helper import get_setting_value
from models.device_instance import DeviceInstance
from models.plugin_object_instance import PluginObjectInstance
# Make sure log level is initialized correctly
Logger(get_setting_value('LOG_LEVEL'))
Logger(get_setting_value("LOG_LEVEL"))
from workflows.triggers import Trigger
class Action:
"""Base class for all actions."""
def __init__(self, trigger):
self.trigger = trigger
def __init__(self, trigger):
self.trigger = trigger
def execute(self, obj):
"""Executes the action on the given object."""
@@ -37,7 +37,10 @@ class UpdateFieldAction(Action):
self.db = db
def execute(self):
mylog('verbose', f"[WF] Updating field '{self.field}' to '{self.value}' for event object {self.trigger.object_type}")
mylog(
"verbose",
f"[WF] Updating field '{self.field}' to '{self.value}' for event object {self.trigger.object_type}",
)
obj = self.trigger.object
@@ -49,19 +52,19 @@ class UpdateFieldAction(Action):
# currently unused
if isinstance(obj, dict) and "ObjectGUID" in obj:
mylog('debug', f"[WF] Updating Object '{obj}' ")
mylog("debug", f"[WF] Updating Object '{obj}' ")
plugin_instance = PluginObjectInstance(self.db)
plugin_instance.updateField(obj["ObjectGUID"], self.field, self.value)
processed = True
elif isinstance(obj, dict) and "devGUID" in obj:
mylog('debug', f"[WF] Updating Device '{obj}' ")
mylog("debug", f"[WF] Updating Device '{obj}' ")
device_instance = DeviceInstance(self.db)
device_instance.updateField(obj["devGUID"], self.field, self.value)
processed = True
if not processed:
mylog('none', f"[WF] Could not process action for object: {obj}")
mylog("none", f"[WF] Could not process action for object: {obj}")
return obj
@@ -74,7 +77,7 @@ class DeleteObjectAction(Action):
self.db = db
def execute(self):
mylog('verbose', f"[WF] Deleting event object {self.trigger.object_type}")
mylog("verbose", f"[WF] Deleting event object {self.trigger.object_type}")
obj = self.trigger.object
@@ -84,21 +87,21 @@ class DeleteObjectAction(Action):
processed = False
# currently unused
# currently unused
if isinstance(obj, dict) and "ObjectGUID" in obj:
mylog('debug', f"[WF] Updating Object '{obj}' ")
mylog("debug", f"[WF] Updating Object '{obj}' ")
plugin_instance = PluginObjectInstance(self.db)
plugin_instance.delete(obj["ObjectGUID"])
processed = True
elif isinstance(obj, dict) and "devGUID" in obj:
mylog('debug', f"[WF] Updating Device '{obj}' ")
mylog("debug", f"[WF] Updating Device '{obj}' ")
device_instance = DeviceInstance(self.db)
device_instance.delete(obj["devGUID"])
processed = True
if not processed:
mylog('none', f"[WF] Could not process action for object: {obj}")
mylog("none", f"[WF] Could not process action for object: {obj}")
return obj
@@ -112,10 +115,14 @@ class RunPluginAction(Action):
self.params = params
def execute(self):
obj = self.trigger.object
mylog('verbose', [f"Executing plugin '{self.plugin_name}' with parameters {self.params} for object {obj}"])
mylog(
"verbose",
[
f"Executing plugin '{self.plugin_name}' with parameters {self.params} for object {obj}"
],
)
# PluginManager.run(self.plugin_name, self.parameters)
return obj
@@ -130,7 +137,12 @@ class SendNotificationAction(Action):
def execute(self):
obj = self.trigger.object
mylog('verbose', [f"Sending notification via '{self.method}': {self.message} for object {obj}"])
mylog(
"verbose",
[
f"Sending notification via '{self.method}': {self.message} for object {obj}"
],
)
# NotificationManager.send(self.method, self.message)
return obj
@@ -144,4 +156,4 @@ class ActionGroup:
def execute(self, obj):
for action in self.actions:
action.execute(obj)
return obj
return obj

View File

@@ -1,37 +1,28 @@
import datetime
import json
import uuid
import os
import sys
import pytz
# Register NetAlertX directories
INSTALL_PATH="/app"
INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
sys.path.extend([f"{INSTALL_PATH}/server"])
# Register NetAlertX modules
import conf
from helper import get_setting_value, timeNowTZ
# Make sure the TIMEZONE for logging is correct
# conf.tz = pytz.timezone(get_setting_value('TIMEZONE'))
from logger import mylog, Logger, logResult
from helper import get_setting_value
from logger import Logger
from const import sql_generateGuid
# Make sure log level is initialized correctly
Logger(get_setting_value('LOG_LEVEL'))
Logger(get_setting_value("LOG_LEVEL"))
from const import applicationPath, logPath, apiPath, confFileName, sql_generateGuid
from helper import timeNowTZ
class AppEvent_obj:
def __init__(self, db):
self.db = db
# Drop existing table
# Drop existing table
self.db.sql.execute("""DROP TABLE IF EXISTS "AppEvents" """)
# Drop all triggers
self.drop_all_triggers()
# Create the AppEvents table if missing
self.create_app_events_table()
@@ -47,7 +38,7 @@ class AppEvent_obj:
"ObjectStatusColumn": "'devPresentLastScan'",
"ObjectIsNew": "NEW.devIsNew",
"ObjectIsArchived": "NEW.devIsArchived",
"ObjectPlugin": "'DEVICES'"
"ObjectPlugin": "'DEVICES'",
}
}
# ,
@@ -66,7 +57,6 @@ class AppEvent_obj:
# }
}
# Re-Create triggers dynamically
for table, config in self.object_mapping.items():
self.create_trigger(table, "insert", config)
@@ -130,8 +120,8 @@ class AppEvent_obj:
SELECT 1 FROM AppEvents
WHERE AppEventProcessed = 0
AND ObjectType = '{table_name}'
AND ObjectGUID = {manage_prefix(config['fields']['ObjectGUID'], event)}
AND ObjectStatus = {manage_prefix(config['fields']['ObjectStatus'], event)}
AND ObjectGUID = {manage_prefix(config["fields"]["ObjectGUID"], event)}
AND ObjectStatus = {manage_prefix(config["fields"]["ObjectStatus"], event)}
AND AppEventType = '{event.lower()}'
)
BEGIN
@@ -156,15 +146,15 @@ class AppEvent_obj:
DATETIME('now'),
FALSE,
'{table_name}',
{manage_prefix(config['fields']['ObjectGUID'], event)}, -- ObjectGUID
{manage_prefix(config['fields']['ObjectPrimaryID'], event)}, -- ObjectPrimaryID
{manage_prefix(config['fields']['ObjectSecondaryID'], event)}, -- ObjectSecondaryID
{manage_prefix(config['fields']['ObjectStatus'], event)}, -- ObjectStatus
{manage_prefix(config['fields']['ObjectStatusColumn'], event)}, -- ObjectStatusColumn
{manage_prefix(config['fields']['ObjectIsNew'], event)}, -- ObjectIsNew
{manage_prefix(config['fields']['ObjectIsArchived'], event)}, -- ObjectIsArchived
{manage_prefix(config['fields']['ObjectForeignKey'], event)}, -- ObjectForeignKey
{manage_prefix(config['fields']['ObjectPlugin'], event)}, -- ObjectForeignKey
{manage_prefix(config["fields"]["ObjectGUID"], event)}, -- ObjectGUID
{manage_prefix(config["fields"]["ObjectPrimaryID"], event)}, -- ObjectPrimaryID
{manage_prefix(config["fields"]["ObjectSecondaryID"], event)}, -- ObjectSecondaryID
{manage_prefix(config["fields"]["ObjectStatus"], event)}, -- ObjectStatus
{manage_prefix(config["fields"]["ObjectStatusColumn"], event)}, -- ObjectStatusColumn
{manage_prefix(config["fields"]["ObjectIsNew"], event)}, -- ObjectIsNew
{manage_prefix(config["fields"]["ObjectIsArchived"], event)}, -- ObjectIsArchived
{manage_prefix(config["fields"]["ObjectForeignKey"], event)}, -- ObjectForeignKey
{manage_prefix(config["fields"]["ObjectPlugin"], event)}, -- ObjectForeignKey
'{event.lower()}'
);
END;
@@ -178,9 +168,9 @@ class AppEvent_obj:
# Commit changes
self.db.commitDB()
# Manage prefixes of column names
def manage_prefix(field, event):
if event == "delete":
return field.replace("NEW.", "OLD.")
return field
return field

View File

@@ -1,17 +1,18 @@
import re
import sys
import json
import os
import sys
# Register NetAlertX directories
INSTALL_PATH="/app"
INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
sys.path.extend([f"{INSTALL_PATH}/server"])
import conf
from logger import mylog, Logger
from helper import get_setting_value, timeNowTZ
from helper import get_setting_value
# Make sure log level is initialized correctly
Logger(get_setting_value('LOG_LEVEL'))
Logger(get_setting_value("LOG_LEVEL"))
class Condition:
"""Evaluates a single condition."""
@@ -23,11 +24,13 @@ class Condition:
self.negate = condition_json.get("negate", False)
def evaluate(self, trigger):
# try finding the value of the field on the event triggering this workflow or thre object triggering the app event
appEvent_value = trigger.event[self.field] if self.field in trigger.event.keys() else None
eveObj_value = trigger.object[self.field] if self.field in trigger.object.keys() else None
appEvent_value = (
trigger.event[self.field] if self.field in trigger.event.keys() else None
)
eveObj_value = (
trigger.object[self.field] if self.field in trigger.object.keys() else None
)
# proceed only if value found
if appEvent_value is None and eveObj_value is None:
@@ -46,7 +49,7 @@ class Condition:
result = bool(re.match(self.value, str(obj_value)))
else:
m = f"[WF] Unsupported operator: {self.operator}"
mylog('none', [m])
mylog("none", [m])
raise ValueError(m)
return not result if self.negate else result
@@ -56,8 +59,10 @@ class ConditionGroup:
"""Handles condition groups with AND, OR logic, supporting nested groups."""
def __init__(self, group_json):
mylog('verbose', [f"[WF] ConditionGroup json.dumps(group_json): {json.dumps(group_json)}"])
mylog(
"verbose",
[f"[WF] ConditionGroup json.dumps(group_json): {json.dumps(group_json)}"],
)
self.logic = group_json.get("logic", "AND").upper()
self.conditions = []
@@ -77,5 +82,5 @@ class ConditionGroup:
return any(results)
else:
m = f"[WF] ConditionGroup unsupported logic: {self.logic}"
mylog('verbose', [m])
mylog("verbose", [m])
raise ValueError(m)

View File

@@ -1,22 +1,21 @@
import sys
import json
import os
import sys
# 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 fullConfFolder
import workflows.actions
from logger import mylog, Logger
from helper import get_setting_value, timeNowTZ
from helper import get_setting_value
# Make sure log level is initialized correctly
Logger(get_setting_value('LOG_LEVEL'))
Logger(get_setting_value("LOG_LEVEL"))
from workflows.triggers import Trigger
from workflows.conditions import ConditionGroup
from workflows.actions import *
from workflows.actions import DeleteObjectAction, RunPluginAction, UpdateFieldAction
class WorkflowManager:
def __init__(self, db):
@@ -27,12 +26,12 @@ class WorkflowManager:
def load_workflows(self):
"""Load workflows from workflows.json."""
try:
workflows_json_path = fullConfFolder + '/workflows.json'
with open(workflows_json_path, 'r') as f:
workflows_json_path = fullConfFolder + "/workflows.json"
with open(workflows_json_path, "r") as f:
workflows = json.load(f)
return workflows
except (FileNotFoundError, json.JSONDecodeError):
mylog('none', ['[WF] Failed to load workflows.json'])
mylog("none", ["[WF] Failed to load workflows.json"])
return []
def get_new_app_events(self):
@@ -43,43 +42,50 @@ class WorkflowManager:
ORDER BY DateTimeCreated ASC
""").fetchall()
mylog('none', [f'[WF] get_new_app_events - new events count: {len(result)}'])
mylog("none", [f"[WF] get_new_app_events - new events count: {len(result)}"])
return result
def process_event(self, event):
"""Process the events. Check if events match a workflow trigger"""
evGuid = event["GUID"]
mylog('verbose', [f"[WF] Processing event with GUID {evGuid}"])
mylog("verbose", [f"[WF] Processing event with GUID {evGuid}"])
# Check if the trigger conditions match
for workflow in self.workflows:
# Ensure workflow is enabled before proceeding
if workflow.get("enabled", "No").lower() == "yes":
wfName = workflow["name"]
mylog('debug', [f"[WF] Checking if '{evGuid}' triggers the workflow '{wfName}'"])
mylog(
"debug",
[f"[WF] Checking if '{evGuid}' triggers the workflow '{wfName}'"],
)
# construct trigger object which also evaluates if the current event triggers it
trigger = Trigger(workflow["trigger"], event, self.db)
if trigger.triggered:
mylog('verbose', [f"[WF] Event with GUID '{evGuid}' triggered the workflow '{wfName}'"])
mylog(
"verbose",
[
f"[WF] Event with GUID '{evGuid}' triggered the workflow '{wfName}'"
],
)
self.execute_workflow(workflow, trigger)
# After processing the event, mark the event as processed (set AppEventProcessed to 1)
self.db.sql.execute("""
self.db.sql.execute(
"""
UPDATE AppEvents
SET AppEventProcessed = 1
WHERE "Index" = ?
""", (event['Index'],)) # Pass the event's unique identifier
""",
(event["Index"],),
) # Pass the event's unique identifier
self.db.commitDB()
def execute_workflow(self, workflow, trigger):
"""Execute the actions in the given workflow if conditions are met."""
@@ -88,25 +94,27 @@ class WorkflowManager:
# Ensure conditions exist
if not isinstance(workflow.get("conditions"), list):
m = f"[WF] workflow['conditions'] must be a list"
mylog('none', [m])
m = "[WF] workflow['conditions'] must be a list"
mylog("none", [m])
raise ValueError(m)
# Evaluate each condition group separately
for condition_group in workflow["conditions"]:
evaluator = ConditionGroup(condition_group)
if evaluator.evaluate(trigger): # If any group evaluates to True
mylog('none', [f"[WF] Workflow {wfName} will be executed - conditions were evaluated as TRUE"])
mylog('debug', [f"[WF] Workflow condition_group: {condition_group}"])
mylog(
"none",
[
f"[WF] Workflow {wfName} will be executed - conditions were evaluated as TRUE"
],
)
mylog("debug", [f"[WF] Workflow condition_group: {condition_group}"])
self.execute_actions(workflow["actions"], trigger)
return # Stop if a condition group succeeds
mylog('none', ["[WF] No condition group matched. Actions not executed."])
mylog("none", ["[WF] No condition group matched. Actions not executed."])
def execute_actions(self, actions, trigger):
"""Execute the actions defined in a workflow."""
@@ -134,7 +142,7 @@ class WorkflowManager:
else:
m = f"[WF] Unsupported action type: {action['type']}"
mylog('none', [m])
mylog("none", [m])
raise ValueError(m)
action_instance.execute() # Execute the action
@@ -147,7 +155,7 @@ class WorkflowManager:
# field = action["field"]
# value = action["value"]
# action_instance = UpdateFieldAction(field, value)
# action_instance.execute(trigger.event)
# action_instance.execute(trigger.event)
# elif action["type"] == "run_plugin":
# # Action type is "run_plugin", so map to RunPluginAction
@@ -164,6 +172,3 @@ class WorkflowManager:
# else:
# # Handle unsupported action types
# raise ValueError(f"Unsupported action type: {action['type']}")

View File

@@ -1,17 +1,17 @@
import sys
import json
import os
import sys
# Register NetAlertX directories
INSTALL_PATH="/app"
INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
sys.path.extend([f"{INSTALL_PATH}/server"])
import conf
from logger import mylog, Logger
from helper import get_setting_value, timeNowTZ
from helper import get_setting_value
from database import get_array_from_sql_rows
# Make sure log level is initialized correctly
Logger(get_setting_value('LOG_LEVEL'))
Logger(get_setting_value("LOG_LEVEL"))
class Trigger:
@@ -21,16 +21,23 @@ class Trigger:
"""
:param name: Friendly name of the trigger
:param triggerJson: JSON trigger object {"object_type":"Devices",event_type":"update"}
:param event: The actual event that the trigger is evaluated against
:param event: The actual event that the trigger is evaluated against
:param db: DB connection in case trigger matches and object needs to be retrieved
"""
self.object_type = triggerJson["object_type"]
self.event_type = triggerJson["event_type"]
self.event = event # Store the triggered event context, if provided
self.triggered = self.object_type == event["ObjectType"] and self.event_type == event["AppEventType"]
mylog('debug', [f"""[WF] self.triggered '{self.triggered}' for event '{get_array_from_sql_rows(event)} and trigger {json.dumps(triggerJson)}' """])
self.triggered = (
self.object_type == event["ObjectType"]
and self.event_type == event["AppEventType"]
)
mylog(
"debug",
[
f"""[WF] self.triggered '{self.triggered}' for event '{get_array_from_sql_rows(event)} and trigger {json.dumps(triggerJson)}' """
],
)
if self.triggered:
# object type corresponds with the DB table name
@@ -42,7 +49,7 @@ class Trigger:
refField = "ObjectGUID"
else:
m = f"[WF] Unsupported object_type: {self.object_type}"
mylog('none', [m])
mylog("none", [m])
raise ValueError(m)
query = f"""
@@ -50,16 +57,14 @@ class Trigger:
{db_table}
WHERE {refField} = '{event["ObjectGUID"]}'
"""
mylog('debug', [query])
mylog("debug", [query])
result = db.sql.execute(query).fetchall()
self.object = result[0]
else:
self.object = None
def set_event(self, event):
"""Set or update the event context for this trigger"""
self.event = event