mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-03-30 23:03:03 -07:00
BE+FE: Unstable devices list (3 status changes in 1h)
Signed-off-by: jokob-sk <jokob.sk@gmail.com>
This commit is contained in:
@@ -314,6 +314,9 @@
|
||||
<li>
|
||||
<a href="devices.php#archived" onclick="forceLoadUrl('devices.php#archived')" > <?= lang("Device_Shortcut_Archived");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="devices.php#unstable_devices" onclick="forceLoadUrl('devices.php#unstable_devices')" > <?= lang("Device_Shortcut_Unstable");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="devices.php#all_devices" onclick="forceLoadUrl('devices.php#all_devices')" > <?= lang("Gen_All_Devices");?> </a>
|
||||
</li>
|
||||
|
||||
@@ -218,6 +218,7 @@
|
||||
"Device_Shortcut_Favorites": "المفضلة",
|
||||
"Device_Shortcut_NewDevices": "أجهزة جديدة",
|
||||
"Device_Shortcut_OnlineChart": "مخطط الاتصال",
|
||||
"Device_Shortcut_Unstable": "",
|
||||
"Device_TableHead_AlertDown": "تنبيه عدم الاتصال",
|
||||
"Device_TableHead_Connected_Devices": "الأجهزة المتصلة",
|
||||
"Device_TableHead_CustomProps": "خصائص مخصصة",
|
||||
@@ -789,4 +790,4 @@
|
||||
"settings_system_label": "نظام",
|
||||
"settings_update_item_warning": "قم بتحديث القيمة أدناه. احرص على اتباع التنسيق السابق. <b>لم يتم إجراء التحقق.</b>",
|
||||
"test_event_tooltip": "احفظ التغييرات أولاً قبل اختبار الإعدادات."
|
||||
}
|
||||
}
|
||||
@@ -218,6 +218,7 @@
|
||||
"Device_Shortcut_Favorites": "Favorits",
|
||||
"Device_Shortcut_NewDevices": "Nous dispositius",
|
||||
"Device_Shortcut_OnlineChart": "Dispositius detectats",
|
||||
"Device_Shortcut_Unstable": "",
|
||||
"Device_TableHead_AlertDown": "Cancel·lar alerta",
|
||||
"Device_TableHead_Connected_Devices": "Connexions",
|
||||
"Device_TableHead_CustomProps": "Props / Accions",
|
||||
|
||||
@@ -218,6 +218,7 @@
|
||||
"Device_Shortcut_Favorites": "",
|
||||
"Device_Shortcut_NewDevices": "",
|
||||
"Device_Shortcut_OnlineChart": "",
|
||||
"Device_Shortcut_Unstable": "",
|
||||
"Device_TableHead_AlertDown": "",
|
||||
"Device_TableHead_Connected_Devices": "",
|
||||
"Device_TableHead_CustomProps": "",
|
||||
|
||||
@@ -222,6 +222,7 @@
|
||||
"Device_Shortcut_Favorites": "Favoriten",
|
||||
"Device_Shortcut_NewDevices": "Neue Geräte",
|
||||
"Device_Shortcut_OnlineChart": "Gerätepräsenz im Laufe der Zeit",
|
||||
"Device_Shortcut_Unstable": "",
|
||||
"Device_TableHead_AlertDown": "Alarm aus",
|
||||
"Device_TableHead_Connected_Devices": "Verbindungen",
|
||||
"Device_TableHead_CustomProps": "Eigenschaften / Aktionen",
|
||||
|
||||
@@ -218,6 +218,7 @@
|
||||
"Device_Shortcut_Favorites": "Favorites",
|
||||
"Device_Shortcut_NewDevices": "New devices",
|
||||
"Device_Shortcut_OnlineChart": "Device presence",
|
||||
"Device_Shortcut_Unstable": "Unstable",
|
||||
"Device_TableHead_AlertDown": "Alert Down",
|
||||
"Device_TableHead_Connected_Devices": "Connections",
|
||||
"Device_TableHead_CustomProps": "Props / Actions",
|
||||
|
||||
@@ -220,6 +220,7 @@
|
||||
"Device_Shortcut_Favorites": "Favorito(s)",
|
||||
"Device_Shortcut_NewDevices": "Nuevos dispositivos",
|
||||
"Device_Shortcut_OnlineChart": "Presencia del dispositivo a lo largo del tiempo",
|
||||
"Device_Shortcut_Unstable": "",
|
||||
"Device_TableHead_AlertDown": "Alerta desactivada",
|
||||
"Device_TableHead_Connected_Devices": "Conexiones",
|
||||
"Device_TableHead_CustomProps": "Propiedades / Acciones",
|
||||
|
||||
@@ -218,6 +218,7 @@
|
||||
"Device_Shortcut_Favorites": "",
|
||||
"Device_Shortcut_NewDevices": "",
|
||||
"Device_Shortcut_OnlineChart": "",
|
||||
"Device_Shortcut_Unstable": "",
|
||||
"Device_TableHead_AlertDown": "",
|
||||
"Device_TableHead_Connected_Devices": "",
|
||||
"Device_TableHead_CustomProps": "",
|
||||
|
||||
@@ -218,6 +218,7 @@
|
||||
"Device_Shortcut_Favorites": "Favoris",
|
||||
"Device_Shortcut_NewDevices": "Nouveaux appareils",
|
||||
"Device_Shortcut_OnlineChart": "Présence de l'appareil",
|
||||
"Device_Shortcut_Unstable": "",
|
||||
"Device_TableHead_AlertDown": "Alerter si En panne",
|
||||
"Device_TableHead_Connected_Devices": "Connexions",
|
||||
"Device_TableHead_CustomProps": "Champs / Actions",
|
||||
@@ -789,4 +790,4 @@
|
||||
"settings_system_label": "Système",
|
||||
"settings_update_item_warning": "Mettre à jour la valeur ci-dessous. Veillez à bien suivre le même format qu'auparavant. <b>Il n'y a pas de pas de contrôle.</b>",
|
||||
"test_event_tooltip": "Enregistrer d'abord vos modifications avant de tester vôtre paramétrage."
|
||||
}
|
||||
}
|
||||
@@ -218,6 +218,7 @@
|
||||
"Device_Shortcut_Favorites": "Preferiti",
|
||||
"Device_Shortcut_NewDevices": "Nuovi dispositivi",
|
||||
"Device_Shortcut_OnlineChart": "Presenza dispositivo",
|
||||
"Device_Shortcut_Unstable": "",
|
||||
"Device_TableHead_AlertDown": "Avviso disconnessione",
|
||||
"Device_TableHead_Connected_Devices": "Connessioni",
|
||||
"Device_TableHead_CustomProps": "Proprietà/Azioni",
|
||||
@@ -789,4 +790,4 @@
|
||||
"settings_system_label": "Sistema",
|
||||
"settings_update_item_warning": "Aggiorna il valore qui sotto. Fai attenzione a seguire il formato precedente. <b>La convalida non viene eseguita.</b>",
|
||||
"test_event_tooltip": "Salva le modifiche prima di provare le nuove impostazioni."
|
||||
}
|
||||
}
|
||||
@@ -218,6 +218,7 @@
|
||||
"Device_Shortcut_Favorites": "お気に入り",
|
||||
"Device_Shortcut_NewDevices": "新規デバイス",
|
||||
"Device_Shortcut_OnlineChart": "デバイス検出",
|
||||
"Device_Shortcut_Unstable": "",
|
||||
"Device_TableHead_AlertDown": "ダウンアラート",
|
||||
"Device_TableHead_Connected_Devices": "接続",
|
||||
"Device_TableHead_CustomProps": "属性 / アクション",
|
||||
@@ -789,4 +790,4 @@
|
||||
"settings_system_label": "システム",
|
||||
"settings_update_item_warning": "以下の値を更新してください。以前のフォーマットに従うよう注意してください。<b>検証は行われません。</b>",
|
||||
"test_event_tooltip": "設定をテストする前に、まず変更を保存してください。"
|
||||
}
|
||||
}
|
||||
@@ -218,6 +218,7 @@
|
||||
"Device_Shortcut_Favorites": "Favoritter",
|
||||
"Device_Shortcut_NewDevices": "Nye Enheter",
|
||||
"Device_Shortcut_OnlineChart": "Enhetens tilstedeværelse",
|
||||
"Device_Shortcut_Unstable": "",
|
||||
"Device_TableHead_AlertDown": "",
|
||||
"Device_TableHead_Connected_Devices": "Tilkoblinger",
|
||||
"Device_TableHead_CustomProps": "",
|
||||
|
||||
@@ -218,6 +218,7 @@
|
||||
"Device_Shortcut_Favorites": "Ulubione",
|
||||
"Device_Shortcut_NewDevices": "Nowe urządzenia",
|
||||
"Device_Shortcut_OnlineChart": "Obecność urządzenia",
|
||||
"Device_Shortcut_Unstable": "",
|
||||
"Device_TableHead_AlertDown": "Alert niedostępny",
|
||||
"Device_TableHead_Connected_Devices": "Połączenia",
|
||||
"Device_TableHead_CustomProps": "Właściwości / Akcje",
|
||||
|
||||
@@ -218,6 +218,7 @@
|
||||
"Device_Shortcut_Favorites": "Favoritos",
|
||||
"Device_Shortcut_NewDevices": "Novos dispositivos",
|
||||
"Device_Shortcut_OnlineChart": "Presença do dispositivo",
|
||||
"Device_Shortcut_Unstable": "",
|
||||
"Device_TableHead_AlertDown": "Alerta em baixo",
|
||||
"Device_TableHead_Connected_Devices": "Conexões",
|
||||
"Device_TableHead_CustomProps": "",
|
||||
|
||||
@@ -218,6 +218,7 @@
|
||||
"Device_Shortcut_Favorites": "Favoritos",
|
||||
"Device_Shortcut_NewDevices": "Novo dispostivo",
|
||||
"Device_Shortcut_OnlineChart": "Presença do dispositivo",
|
||||
"Device_Shortcut_Unstable": "",
|
||||
"Device_TableHead_AlertDown": "Alerta em baixo",
|
||||
"Device_TableHead_Connected_Devices": "Conexões",
|
||||
"Device_TableHead_CustomProps": "Propriedades / Ações",
|
||||
|
||||
@@ -218,6 +218,7 @@
|
||||
"Device_Shortcut_Favorites": "Избранные",
|
||||
"Device_Shortcut_NewDevices": "Новые устройства",
|
||||
"Device_Shortcut_OnlineChart": "Присутствие устройств",
|
||||
"Device_Shortcut_Unstable": "",
|
||||
"Device_TableHead_AlertDown": "Оповещение о сост. ВЫКЛ",
|
||||
"Device_TableHead_Connected_Devices": "Соединения",
|
||||
"Device_TableHead_CustomProps": "Свойства / Действия",
|
||||
@@ -789,4 +790,4 @@
|
||||
"settings_system_label": "Система",
|
||||
"settings_update_item_warning": "Обновить значение ниже. Будьте осторожны, следуя предыдущему формату. <b>Проверка не выполняется.</b>",
|
||||
"test_event_tooltip": "Сначала сохраните изменения, прежде чем проверять настройки."
|
||||
}
|
||||
}
|
||||
@@ -218,6 +218,7 @@
|
||||
"Device_Shortcut_Favorites": "",
|
||||
"Device_Shortcut_NewDevices": "",
|
||||
"Device_Shortcut_OnlineChart": "",
|
||||
"Device_Shortcut_Unstable": "",
|
||||
"Device_TableHead_AlertDown": "",
|
||||
"Device_TableHead_Connected_Devices": "",
|
||||
"Device_TableHead_CustomProps": "",
|
||||
|
||||
@@ -218,6 +218,7 @@
|
||||
"Device_Shortcut_Favorites": "Favoriler",
|
||||
"Device_Shortcut_NewDevices": "Yeni Cİhazlar",
|
||||
"Device_Shortcut_OnlineChart": "Cihaz Durumu",
|
||||
"Device_Shortcut_Unstable": "",
|
||||
"Device_TableHead_AlertDown": "Çalışmama Alarmı",
|
||||
"Device_TableHead_Connected_Devices": "Bağlantılar",
|
||||
"Device_TableHead_CustomProps": "Özellikler / Eylemler",
|
||||
|
||||
@@ -218,6 +218,7 @@
|
||||
"Device_Shortcut_Favorites": "Вибране",
|
||||
"Device_Shortcut_NewDevices": "Нові пристрої",
|
||||
"Device_Shortcut_OnlineChart": "Наявність пристрою",
|
||||
"Device_Shortcut_Unstable": "",
|
||||
"Device_TableHead_AlertDown": "Агент Вниз",
|
||||
"Device_TableHead_Connected_Devices": "Зв'язки",
|
||||
"Device_TableHead_CustomProps": "Реквізит / дії",
|
||||
@@ -789,4 +790,4 @@
|
||||
"settings_system_label": "Система",
|
||||
"settings_update_item_warning": "Оновіть значення нижче. Слідкуйте за попереднім форматом. <b>Перевірка не виконана.</b>",
|
||||
"test_event_tooltip": "Перш ніж перевіряти налаштування, збережіть зміни."
|
||||
}
|
||||
}
|
||||
@@ -218,6 +218,7 @@
|
||||
"Device_Shortcut_Favorites": "",
|
||||
"Device_Shortcut_NewDevices": "",
|
||||
"Device_Shortcut_OnlineChart": "",
|
||||
"Device_Shortcut_Unstable": "",
|
||||
"Device_TableHead_AlertDown": "",
|
||||
"Device_TableHead_Connected_Devices": "",
|
||||
"Device_TableHead_CustomProps": "",
|
||||
|
||||
@@ -218,6 +218,7 @@
|
||||
"Device_Shortcut_Favorites": "收藏",
|
||||
"Device_Shortcut_NewDevices": "新设备",
|
||||
"Device_Shortcut_OnlineChart": "设备统计",
|
||||
"Device_Shortcut_Unstable": "",
|
||||
"Device_TableHead_AlertDown": "提醒宕机",
|
||||
"Device_TableHead_Connected_Devices": "链接",
|
||||
"Device_TableHead_CustomProps": "属性",
|
||||
@@ -789,4 +790,4 @@
|
||||
"settings_system_label": "系统",
|
||||
"settings_update_item_warning": "更新下面的值。请注意遵循先前的格式。<b>未执行验证。</b>",
|
||||
"test_event_tooltip": "在测试设置之前,请先保存更改。"
|
||||
}
|
||||
}
|
||||
@@ -100,6 +100,7 @@ class Device(ObjectType):
|
||||
devParentPortSource = String(description="Source tracking for devParentPort (USER, LOCKED, NEWDEV, or plugin prefix)")
|
||||
devParentRelTypeSource = String(description="Source tracking for devParentRelType (USER, LOCKED, NEWDEV, or plugin prefix)")
|
||||
devVlanSource = String(description="Source tracking for devVlan")
|
||||
devFlapping = String(description="ndicates flapping device (device changing between online/offline states frequently)")
|
||||
|
||||
|
||||
class DeviceResult(ObjectType):
|
||||
@@ -266,7 +267,7 @@ class Query(ObjectType):
|
||||
filtered.append(device)
|
||||
|
||||
devices_data = filtered
|
||||
# 🔻 START If you change anything here, also update get_device_condition_by_status
|
||||
# 🔻 START If you change anything here, also update get_device_conditions
|
||||
elif status == "connected":
|
||||
devices_data = [
|
||||
device
|
||||
@@ -323,7 +324,25 @@ class Query(ObjectType):
|
||||
for device in devices_data
|
||||
if device["devType"] in network_dev_types and device["devPresentLastScan"] == 0 and device["devIsArchived"] == 0
|
||||
]
|
||||
# 🔺 END If you change anything here, also update get_device_condition_by_status
|
||||
elif status == "unstable_devices":
|
||||
devices_data = [
|
||||
device
|
||||
for device in devices_data
|
||||
if device["devIsArchived"] == 0 and device["devFlapping"] == 1
|
||||
]
|
||||
elif status == "unstable_favorites":
|
||||
devices_data = [
|
||||
device
|
||||
for device in devices_data
|
||||
if device["devIsArchived"] == 0 and device["devFavorite"] == 1 and device["devFlapping"] == 1
|
||||
]
|
||||
elif status == "unstable_network_devices":
|
||||
devices_data = [
|
||||
device
|
||||
for device in devices_data
|
||||
if device["devIsArchived"] == 0 and device["devType"] in network_dev_types and device["devFlapping"] == 1
|
||||
]
|
||||
# 🔺 END If you change anything here, also update get_device_conditions
|
||||
elif status == "all_devices":
|
||||
devices_data = devices_data # keep all
|
||||
|
||||
|
||||
@@ -58,70 +58,13 @@ NULL_EQUIVALENTS = ["", "null", "(unknown)", "(Unknown)", "(name not found)"]
|
||||
# Convert list to SQL string: wrap each value in single quotes and escape single quotes if needed
|
||||
NULL_EQUIVALENTS_SQL = ",".join("'" + v.replace("'", "''") + "'" for v in NULL_EQUIVALENTS)
|
||||
|
||||
|
||||
# ===============================================================================
|
||||
# SQL queries
|
||||
# ===============================================================================
|
||||
sql_devices_all = """
|
||||
SELECT
|
||||
rowid,
|
||||
IFNULL(devMac, '') AS devMac,
|
||||
IFNULL(devName, '') AS devName,
|
||||
IFNULL(devOwner, '') AS devOwner,
|
||||
IFNULL(devType, '') AS devType,
|
||||
IFNULL(devVendor, '') AS devVendor,
|
||||
IFNULL(devFavorite, '') AS devFavorite,
|
||||
IFNULL(devGroup, '') AS devGroup,
|
||||
IFNULL(devComments, '') AS devComments,
|
||||
IFNULL(devFirstConnection, '') AS devFirstConnection,
|
||||
IFNULL(devLastConnection, '') AS devLastConnection,
|
||||
IFNULL(devLastIP, '') AS devLastIP,
|
||||
IFNULL(devPrimaryIPv4, '') AS devPrimaryIPv4,
|
||||
IFNULL(devPrimaryIPv6, '') AS devPrimaryIPv6,
|
||||
IFNULL(devVlan, '') AS devVlan,
|
||||
IFNULL(devForceStatus, '') AS devForceStatus,
|
||||
IFNULL(devStaticIP, '') AS devStaticIP,
|
||||
IFNULL(devScan, '') AS devScan,
|
||||
IFNULL(devLogEvents, '') AS devLogEvents,
|
||||
IFNULL(devAlertEvents, '') AS devAlertEvents,
|
||||
IFNULL(devAlertDown, '') AS devAlertDown,
|
||||
IFNULL(devSkipRepeated, '') AS devSkipRepeated,
|
||||
IFNULL(devLastNotification, '') AS devLastNotification,
|
||||
IFNULL(devPresentLastScan, 0) AS devPresentLastScan,
|
||||
IFNULL(devIsNew, '') AS devIsNew,
|
||||
IFNULL(devLocation, '') AS devLocation,
|
||||
IFNULL(devIsArchived, '') AS devIsArchived,
|
||||
IFNULL(devParentMAC, '') AS devParentMAC,
|
||||
IFNULL(devParentPort, '') AS devParentPort,
|
||||
IFNULL(devIcon, '') AS devIcon,
|
||||
IFNULL(devGUID, '') AS devGUID,
|
||||
IFNULL(devSite, '') AS devSite,
|
||||
IFNULL(devSSID, '') AS devSSID,
|
||||
IFNULL(devSyncHubNode, '') AS devSyncHubNode,
|
||||
IFNULL(devSourcePlugin, '') AS devSourcePlugin,
|
||||
IFNULL(devCustomProps, '') AS devCustomProps,
|
||||
IFNULL(devFQDN, '') AS devFQDN,
|
||||
IFNULL(devParentRelType, '') AS devParentRelType,
|
||||
IFNULL(devReqNicsOnline, '') AS devReqNicsOnline,
|
||||
IFNULL(devMacSource, '') AS devMacSource,
|
||||
IFNULL(devNameSource, '') AS devNameSource,
|
||||
IFNULL(devFQDNSource, '') AS devFQDNSource,
|
||||
IFNULL(devLastIPSource, '') AS devLastIPSource,
|
||||
IFNULL(devVendorSource, '') AS devVendorSource,
|
||||
IFNULL(devSSIDSource, '') AS devSSIDSource,
|
||||
IFNULL(devParentMACSource, '') AS devParentMACSource,
|
||||
IFNULL(devParentPortSource, '') AS devParentPortSource,
|
||||
IFNULL(devParentRelTypeSource, '') AS devParentRelTypeSource,
|
||||
IFNULL(devVlanSource, '') AS devVlanSource,
|
||||
CASE
|
||||
WHEN devIsNew = 1 THEN 'New'
|
||||
WHEN devPresentLastScan = 1 THEN 'On-line'
|
||||
WHEN devPresentLastScan = 0 AND devAlertDown != 0 THEN 'Down'
|
||||
WHEN devIsArchived = 1 THEN 'Archived'
|
||||
WHEN devPresentLastScan = 0 THEN 'Off-line'
|
||||
ELSE 'Unknown status'
|
||||
END AS devStatus
|
||||
FROM Devices
|
||||
sql_devices_all = """
|
||||
SELECT
|
||||
*
|
||||
FROM DevicesView
|
||||
"""
|
||||
|
||||
sql_appevents = """select * from AppEvents order by DateTimeCreated desc"""
|
||||
|
||||
@@ -14,22 +14,28 @@ from const import NULL_EQUIVALENTS_SQL # noqa: E402 [flake8 lint suppression]
|
||||
def get_device_conditions():
|
||||
network_dev_types = ",".join("'" + v.replace("'", "''") + "'" for v in get_setting_value("NETWORK_DEVICE_TYPES"))
|
||||
|
||||
# DO NOT CHANGE ORDER
|
||||
# Base archived condition
|
||||
base_active = "devIsArchived=0"
|
||||
|
||||
# DO NOT CHANGE ORDER - if you add or change something update graphql endpoint as well
|
||||
conditions = {
|
||||
"all": "WHERE devIsArchived=0",
|
||||
"my": "WHERE devIsArchived=0",
|
||||
"all": f"WHERE {base_active}",
|
||||
"my": f"WHERE {base_active}",
|
||||
"connected": "WHERE devPresentLastScan=1",
|
||||
"favorites": "WHERE devIsArchived=0 AND devFavorite=1",
|
||||
"new": "WHERE devIsArchived=0 AND devIsNew=1",
|
||||
"down": "WHERE devIsArchived=0 AND devAlertDown != 0 AND devPresentLastScan=0",
|
||||
"offline": "WHERE devIsArchived=0 AND devPresentLastScan=0",
|
||||
"favorites": f"WHERE {base_active} AND devFavorite=1",
|
||||
"new": f"WHERE {base_active} AND devIsNew=1",
|
||||
"down": f"WHERE {base_active} AND devAlertDown != 0 AND devPresentLastScan=0",
|
||||
"offline": f"WHERE {base_active} AND devPresentLastScan=0",
|
||||
"archived": "WHERE devIsArchived=1",
|
||||
"network_devices": f"WHERE devIsArchived=0 AND devType in ({network_dev_types})",
|
||||
"network_devices_down": f"WHERE devIsArchived=0 AND devType in ({network_dev_types}) AND devPresentLastScan=0",
|
||||
"unknown": f"WHERE devIsArchived=0 AND devName in ({NULL_EQUIVALENTS_SQL})",
|
||||
"known": f"WHERE devIsArchived=0 AND devName not in ({NULL_EQUIVALENTS_SQL})",
|
||||
"favorites_offline": "WHERE devIsArchived=0 AND devFavorite=1 AND devPresentLastScan=0",
|
||||
"new_online": "WHERE devIsArchived=0 AND devIsNew=1 AND devPresentLastScan=0",
|
||||
"network_devices": f"WHERE {base_active} AND devType IN ({network_dev_types})",
|
||||
"network_devices_down": f"WHERE {base_active} AND devType IN ({network_dev_types}) AND devPresentLastScan=0",
|
||||
"unknown": f"WHERE {base_active} AND devName IN ({NULL_EQUIVALENTS_SQL})",
|
||||
"known": f"WHERE {base_active} AND devName NOT IN ({NULL_EQUIVALENTS_SQL})",
|
||||
"favorites_offline": f"WHERE {base_active} AND devFavorite=1 AND devPresentLastScan=0",
|
||||
"new_online": f"WHERE {base_active} AND devIsNew=1 AND devPresentLastScan=0",
|
||||
"unstable_devices": f"WHERE {base_active} AND devFlapping=1",
|
||||
"unstable_favorites": f"WHERE {base_active} AND devFavorite=1 AND devFlapping=1",
|
||||
"unstable_network_devices": f"WHERE {base_active} AND devType IN ({network_dev_types}) AND devFlapping=1",
|
||||
}
|
||||
|
||||
return conditions
|
||||
|
||||
@@ -232,6 +232,87 @@ def ensure_views(sql) -> bool:
|
||||
|
||||
""")
|
||||
|
||||
FLAP_THRESHOLD = 3
|
||||
FLAP_WINDOW_HOURS = 1
|
||||
|
||||
sql.execute(""" DROP VIEW IF EXISTS DevicesView;""")
|
||||
sql.execute(f""" CREATE VIEW DevicesView AS
|
||||
SELECT
|
||||
rowid,
|
||||
IFNULL(devMac, '') AS devMac,
|
||||
IFNULL(devName, '') AS devName,
|
||||
IFNULL(devOwner, '') AS devOwner,
|
||||
IFNULL(devType, '') AS devType,
|
||||
IFNULL(devVendor, '') AS devVendor,
|
||||
IFNULL(devFavorite, '') AS devFavorite,
|
||||
IFNULL(devGroup, '') AS devGroup,
|
||||
IFNULL(devComments, '') AS devComments,
|
||||
IFNULL(devFirstConnection, '') AS devFirstConnection,
|
||||
IFNULL(devLastConnection, '') AS devLastConnection,
|
||||
IFNULL(devLastIP, '') AS devLastIP,
|
||||
IFNULL(devPrimaryIPv4, '') AS devPrimaryIPv4,
|
||||
IFNULL(devPrimaryIPv6, '') AS devPrimaryIPv6,
|
||||
IFNULL(devVlan, '') AS devVlan,
|
||||
IFNULL(devForceStatus, '') AS devForceStatus,
|
||||
IFNULL(devStaticIP, '') AS devStaticIP,
|
||||
IFNULL(devScan, '') AS devScan,
|
||||
IFNULL(devLogEvents, '') AS devLogEvents,
|
||||
IFNULL(devAlertEvents, '') AS devAlertEvents,
|
||||
IFNULL(devAlertDown, '') AS devAlertDown,
|
||||
IFNULL(devSkipRepeated, '') AS devSkipRepeated,
|
||||
IFNULL(devLastNotification, '') AS devLastNotification,
|
||||
IFNULL(devPresentLastScan, 0) AS devPresentLastScan,
|
||||
IFNULL(devIsNew, '') AS devIsNew,
|
||||
IFNULL(devLocation, '') AS devLocation,
|
||||
IFNULL(devIsArchived, '') AS devIsArchived,
|
||||
IFNULL(devParentMAC, '') AS devParentMAC,
|
||||
IFNULL(devParentPort, '') AS devParentPort,
|
||||
IFNULL(devIcon, '') AS devIcon,
|
||||
IFNULL(devGUID, '') AS devGUID,
|
||||
IFNULL(devSite, '') AS devSite,
|
||||
IFNULL(devSSID, '') AS devSSID,
|
||||
IFNULL(devSyncHubNode, '') AS devSyncHubNode,
|
||||
IFNULL(devSourcePlugin, '') AS devSourcePlugin,
|
||||
IFNULL(devCustomProps, '') AS devCustomProps,
|
||||
IFNULL(devFQDN, '') AS devFQDN,
|
||||
IFNULL(devParentRelType, '') AS devParentRelType,
|
||||
IFNULL(devReqNicsOnline, '') AS devReqNicsOnline,
|
||||
IFNULL(devMacSource, '') AS devMacSource,
|
||||
IFNULL(devNameSource, '') AS devNameSource,
|
||||
IFNULL(devFQDNSource, '') AS devFQDNSource,
|
||||
IFNULL(devLastIPSource, '') AS devLastIPSource,
|
||||
IFNULL(devVendorSource, '') AS devVendorSource,
|
||||
IFNULL(devSSIDSource, '') AS devSSIDSource,
|
||||
IFNULL(devParentMACSource, '') AS devParentMACSource,
|
||||
IFNULL(devParentPortSource, '') AS devParentPortSource,
|
||||
IFNULL(devParentRelTypeSource, '') AS devParentRelTypeSource,
|
||||
IFNULL(devVlanSource, '') AS devVlanSource,
|
||||
CASE
|
||||
WHEN devIsNew = 1 THEN 'New'
|
||||
WHEN devPresentLastScan = 1 THEN 'On-line'
|
||||
WHEN devPresentLastScan = 0 AND devAlertDown != 0 THEN 'Down'
|
||||
WHEN devIsArchived = 1 THEN 'Archived'
|
||||
WHEN devPresentLastScan = 0 THEN 'Off-line'
|
||||
ELSE 'Unknown status'
|
||||
END AS devStatus,
|
||||
CASE
|
||||
WHEN EXISTS (
|
||||
SELECT 1
|
||||
FROM Events e
|
||||
WHERE e.eve_MAC = Devices.devMac
|
||||
AND e.eve_EventType IN ('Connected','Disconnected','Device Down','Down Reconnected')
|
||||
AND e.eve_DateTime >= datetime('now', '-{FLAP_WINDOW_HOURS} hours')
|
||||
GROUP BY e.eve_MAC
|
||||
HAVING COUNT(*) >= {FLAP_THRESHOLD}
|
||||
)
|
||||
THEN 1
|
||||
ELSE 0
|
||||
END AS devFlapping
|
||||
|
||||
FROM Devices
|
||||
|
||||
""")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@@ -338,7 +338,7 @@ class DeviceInstance:
|
||||
for key, condition in conditions.items():
|
||||
# Make sure the alias is SQL-safe (no spaces or special chars)
|
||||
alias = key.replace(" ", "_").lower()
|
||||
sub_queries.append(f'(SELECT COUNT(*) FROM Devices {condition}) AS "{alias}"')
|
||||
sub_queries.append(f'(SELECT COUNT(*) FROM DevicesView {condition}) AS "{alias}"')
|
||||
|
||||
# Join all sub-selects with commas
|
||||
query = "SELECT\n " + ",\n ".join(sub_queries)
|
||||
@@ -360,7 +360,7 @@ class DeviceInstance:
|
||||
for key, condition in conditions.items():
|
||||
# Make sure the alias is SQL-safe (no spaces or special chars)
|
||||
alias = key.replace(" ", "_").lower()
|
||||
sub_queries.append(f'(SELECT COUNT(*) FROM Devices {condition}) AS "{alias}"')
|
||||
sub_queries.append(f'(SELECT COUNT(*) FROM DevicesView {condition}) AS "{alias}"')
|
||||
|
||||
# Join all sub-selects with commas
|
||||
query = "SELECT\n " + ",\n ".join(sub_queries)
|
||||
@@ -381,7 +381,8 @@ class DeviceInstance:
|
||||
# Build condition for SQL
|
||||
condition = get_device_condition_by_status(status) if status else ""
|
||||
|
||||
query = f"SELECT * FROM Devices {condition}"
|
||||
# Only DevicesView has devFlapping
|
||||
query = f"SELECT * FROM DevicesView {condition}"
|
||||
sql.execute(query)
|
||||
|
||||
table_data = []
|
||||
|
||||
@@ -218,3 +218,50 @@ class EventInstance:
|
||||
|
||||
# Return as list
|
||||
return [row[0], row[1], row[2], row[3], row[4], row[5]]
|
||||
|
||||
def get_unstable_devices(self, hours: int = 1, threshold: int = 3, macs_only: bool = True):
|
||||
"""
|
||||
Return unstable devices based on flap detection.
|
||||
|
||||
A device is considered unstable if it has >= threshold events within the last `hours`.
|
||||
|
||||
Events considered:
|
||||
- Connected
|
||||
- Disconnected
|
||||
- Device Down
|
||||
- Down Reconnected
|
||||
|
||||
Args:
|
||||
hours (int): Time window in hours (default: 1)
|
||||
threshold (int): Minimum number of events to be considered unstable (default: 3)
|
||||
macs_only (bool): If True, return only MAC addresses (set). Otherwise return full rows.
|
||||
|
||||
Returns:
|
||||
set[str] OR list[dict]
|
||||
"""
|
||||
|
||||
if hours <= 0 or threshold <= 0:
|
||||
mylog("warn", f"[Events] get_unstable_devices invalid params: hours={hours}, threshold={threshold}")
|
||||
return set() if macs_only else []
|
||||
|
||||
conn = self._conn()
|
||||
|
||||
sql = """
|
||||
SELECT eve_MAC, COUNT(*) as event_count
|
||||
FROM Events
|
||||
WHERE eve_EventType IN ('Connected','Disconnected','Device Down','Down Reconnected')
|
||||
AND eve_DateTime >= datetime('now', ?)
|
||||
GROUP BY eve_MAC
|
||||
HAVING COUNT(*) >= ?
|
||||
"""
|
||||
|
||||
# SQLite expects "-1 hours" format
|
||||
window = f"-{hours} hours"
|
||||
|
||||
rows = conn.execute(sql, (window, threshold)).fetchall()
|
||||
conn.close()
|
||||
|
||||
if macs_only:
|
||||
return {row["eve_MAC"] for row in rows}
|
||||
|
||||
return [dict(row) for row in rows]
|
||||
|
||||
@@ -49,7 +49,7 @@ class PluginObjectInstance:
|
||||
"SELECT * FROM Plugins_Objects WHERE Plugin = ?", (plugin,)
|
||||
)
|
||||
|
||||
def getLastNCreatedPerPLugin(self, plugin, entries=1):
|
||||
def getLastNCreatedPerPlugin(self, plugin, entries=1):
|
||||
return self._fetchall(
|
||||
"""
|
||||
SELECT *
|
||||
|
||||
Reference in New Issue
Block a user