Skip to content

Commit 2ca5a80

Browse files
authored
Merge pull request #1665 from netalertx/next_release
Next release
2 parents 8f1da10 + f1a9109 commit 2ca5a80

9 files changed

Lines changed: 100 additions & 42 deletions

File tree

docs/ADVISORY_MULTI_NETWORK.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Effective multi-network monitoring starts with understanding how NetAlertX "sees
77
* **A. Understand Network Accessibility:** Local ARP-based scanning (**ARPSCAN**) only discovers devices on directly accessible subnets due to Layer 2 limitations. It cannot traverse VPNs or routed borders without specific configuration.
88
* **B. Plan Subnet & Scan Interfaces:** Explicitly configure each accessible segment in `SCAN_SUBNETS` with the corresponding interfaces.
99
* **C. Remote & Inaccessible Networks:** For networks unreachable via ARP, use these strategies:
10-
* **Alternate Plugins:** Supplement discovery with [SNMPDSC](SNMPDSC) or [DHCP lease imports](https://docs.netalertx.com/PLUGINS/?h=DHCPLSS#available-plugins).
10+
* **Alternate Plugins:** Supplement discovery with [SNMPDSC](https://docs.netalertx.com/PLUGINS/?h=SNMPDSC#available-plugins) or [DHCP lease imports](https://docs.netalertx.com/PLUGINS/?h=DHCPLSS#available-plugins).
1111
* **Sync Hub for MSP & Multi-Site Deployments:** Run secondary NetAlertX instances on isolated networks and aggregate data using the **SYNC plugin**. Use the [`SYNC_BEHAVIOR`](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/sync/README.md#hub-device-write-behavior-sync_behavior) setting on the hub to control whether the hub inherits device config from nodes or manages it independently.
1212
* **Manual Entry:** For static assets where only ICMP (ping) status is needed.
1313

@@ -110,7 +110,7 @@ Don't let a massive device list overwhelm you. Use the [Multi-edit features](./D
110110
As your environment grows, tuning the underlying engine is vital to maintain a snappy UI and reliable discovery cycles.
111111

112112
* **Plugin Scheduling:** Avoid "Scan Storms" by staggering plugin execution. Running intensive tasks like `NMAP` or `MASS_DNS` simultaneously can spike CPU and cause database locks.
113-
* **Database Health:** Large-scale monitoring generates massive event logs. Use the **[DBCLNP (Database Cleanup)](https://www.google.com/search?q=https://docs.netalertx.com/PLUGINS/%23dbclnp)** plugin to prune old records and keep the SQLite database performant.
113+
* **Database Health:** Large-scale monitoring generates massive event logs. Use the **[DBCLNP (Database Cleanup)](https://docs.netalertx.com/PLUGINS/?h=dbclnp#available-plugins)** plugin to prune old records and keep the SQLite database performant.
114114
* **Resource Management:** For high-device counts, consider increasing the memory limit for the container and utilizing `tmpfs` for temporary files to reduce SD card/disk I/O bottlenecks.
115115
* Enable the `DEEP_SLEEP` setting.
116116

docs/ADVISORY_MULTI_SITE_MONITORING.md

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,11 @@ PUSH mode is typically recommended for MSP deployments because remote customer e
7777

7878
The hub can operate in different synchronization ownership modes depending on your operational requirements.
7979

80-
| Mode | Best For |
81-
| -------------- | -------------------------------------------------------------------- |
82-
| `copy-new` | MSP environments where the hub becomes the long-term source of truth |
83-
| `carbon-copy` | Fully managed remote appliances where nodes remain authoritative |
84-
| `hub-defaults` | Centralized inventory management with hub-defined policies |
80+
| Mode | Best For |
81+
| -------------- | --------------------------------------------------------------------- |
82+
| `copy-new` | After initial discovery the hub becomes the long-term source of truth |
83+
| `carbon-copy` | Fully managed remote appliances where nodes remain authoritative |
84+
| `hub-defaults` | Centralized inventory management with hub-defined policies |
8585

8686
This flexibility allows NetAlertX to support both:
8787

@@ -121,14 +121,13 @@ For best results in multi-site environments:
121121
* Use predefined "Down Devices" dashboards
122122
* Enable Prometheus metrics export
123123
* Use UI Filters to create site-specific views
124-
* Configure notification throttling to reduce alert fatigue
125124

126125
---
127126

128127
# Related Documentation
129128

130129
* [Remote Networks](./REMOTE_NETWORKS.md)
131-
* [Sync Hub Plugin](../front/plugins/sync/README.md)
130+
* [Sync Hub Plugin](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/sync/README.md)
132131
* [Workflows](./WORKFLOWS.md)
133132
* [Metrics API](./API_METRICS.md)
134133
* [Eyes on Glass / NOC Dashboard](./ADVISORY_EYES_ON_GLASS.md)

docs/CUSTOM_PROPERTIES.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,22 @@ This functionality allows you to define **custom properties** for devices, which
2121
Visible properties (`CUSTPROP_show: true`) are displayed as interactive icons in the device listing. Each icon can perform one of the following actions based on the `CUSTPROP_type`:
2222

2323
1. **Modals (e.g., Show Notes)**:
24+
2425
- Displays detailed information in a popup modal.
2526
- Example: Firmware version details.
2627

2728
2. **Links**:
29+
2830
- Redirect to an external or internal URL.
2931
- Example: Open a device's documentation or external site.
3032

3133
3. **Device Actions**:
34+
3235
- Manage devices with actions like delete.
3336
- Example: Quickly remove a device from the network.
3437

3538
4. **Plugins**:
39+
3640
- Future placeholder for running custom plugin scripts.
3741
- **Note**: Not implemented yet.
3842

@@ -41,12 +45,15 @@ Visible properties (`CUSTPROP_show: true`) are displayed as interactive icons in
4145
## Example Use Cases
4246

4347
1. **Device Documentation Link**:
48+
4449
- Add a custom property with `CUSTPROP_type` set to `link` or `link_new_tab` to allow quick navigation to the external documentation of the device.
4550

4651
2. **Firmware Details**:
52+
4753
- Use `CUSTPROP_type: show_notes` to display firmware versions or upgrade instructions in a modal.
4854

4955
3. **Device Removal**:
56+
5057
- Enable device removal functionality using `CUSTPROP_type: delete_dev`.
5158

5259
---

docs/DEV_ENV_SETUP.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@ Before starting development, please review the following guidelines.
1818
The application architecture is designed for extensibility and maintainability. It relies heavily on configuration manifests via plugins and settings to dynamically build the UI and populate the application with data from various sources.
1919

2020
For details, see:
21+
2122
- [Plugins Development](PLUGINS_DEV.md) (includes video)
2223
- [Settings System](SETTINGS_SYSTEM.md)
2324

2425
Focus on **core functionality** and integrate with existing tools rather than reinventing the wheel.
2526

2627
Examples:
28+
2729
- Using **Apprise** for notifications instead of implementing multiple separate gateways
2830
- Implementing **regex-based validation** instead of one-off validation for each setting
2931

docs/PLUGINS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ NetAlertX supports additional plugins to extend its functionality, each with its
1212
> ![Loaded plugins settings](./img/PLUGINS/enable_plugin.gif)
1313
1414
1. Pick your `🔍 dev scanner` plugin (e.g. `ARPSCAN` or `NMAPDEV`), or import devices into the application with an `📥 importer` plugin. (See **Enabling plugins** below)
15-
2. Pick a `▶️ publisher` plugin, if you want to send notifications. If you don't see a publisher you'd like to use, look at the [📚_publisher_apprise](/front/plugins/_publisher_apprise/) plugin which is a proxy for over 80 notification services.
15+
2. Pick a `▶️ publisher` plugin, if you want to send notifications. If you don't see a publisher you'd like to use, look at the [📚_publisher_apprise](https://docs.netalertx.com/PLUGINS/?h=APPRISE#available-plugins) plugin which is a proxy for over 80 notification services.
1616
3. Setup your [Network topology diagram](./NETWORK_TREE.md)
1717
4. Fine-tune [Notifications](./NOTIFICATIONS.md)
1818
5. Setup [Workflows](./WORKFLOWS.md)

docs/SUBNETS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ You need to specify the network interface and the network mask. You can also con
3434
> If the timeout is too short, you may see timeout errors in the log. To prevent the application from hanging due to unresponsive plugins, scans are canceled when they exceed the timeout limit.
3535
>
3636
> To fix this:
37+
>
3738
> - Reduce the subnet size (e.g., change `/16` to `/24`).
3839
> - Increase the timeout (e.g., set `ARPSCAN_RUN_TIMEOUT` to `300` for a 5-minute timeout).
3940
> - Extend the scan interval (e.g., set `ARPSCAN_RUN_SCHD` to `*/10 * * * *` to scan every 10 minutes).

front/plugins/icmp_scan/icmp.py

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
1313
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
1414

15-
from plugin_helper import Plugin_Objects # noqa: E402 [flake8 lint suppression]
15+
from plugin_helper import Plugin_Objects, parse_scan_subnets # noqa: E402 [flake8 lint suppression]
1616
from logger import mylog, Logger # noqa: E402 [flake8 lint suppression]
1717
from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
1818
from const import logPath # noqa: E402 [flake8 lint suppression]
@@ -34,18 +34,6 @@
3434
RESULT_FILE = os.path.join(LOG_PATH, f'last_result.{pluginName}.log')
3535

3636

37-
def parse_scan_subnets(subnets):
38-
"""Extract subnet and interface from SCAN_SUBNETS"""
39-
ranges = []
40-
interfaces = []
41-
for entry in subnets:
42-
parts = entry.split("--interface=")
43-
ranges.append(parts[0].strip())
44-
if len(parts) > 1:
45-
interfaces.append(parts[1].strip())
46-
return ranges, interfaces
47-
48-
4937
def get_device_by_ip(ip, all_devices):
5038
"""Get existing device based on IP"""
5139
for device in all_devices:
@@ -66,7 +54,14 @@ def main():
6654
fakeMac = get_setting_value('ICMP_FAKE_MAC')
6755
scan_subnets = get_setting_value("SCAN_SUBNETS")
6856

69-
subnets, interfaces = parse_scan_subnets(scan_subnets)
57+
parsed = parse_scan_subnets(scan_subnets)
58+
59+
subnets = [x.subnet for x in parsed]
60+
interfaces = [
61+
x.resolved_interface
62+
for x in parsed
63+
if x.resolved_interface
64+
]
7065

7166
# Initialize the Plugin obj output file
7267
plugin_objects = Plugin_Objects(RESULT_FILE)

front/plugins/ipneigh/ipneigh.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
1111
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
1212

13-
from plugin_helper import Plugin_Objects # noqa: E402 [flake8 lint suppression]
13+
from plugin_helper import Plugin_Objects, parse_scan_subnets # noqa: E402 [flake8 lint suppression]
1414
from logger import mylog, Logger # noqa: E402 [flake8 lint suppression]
1515
from utils.datetime_utils import timeNowUTC # noqa: E402 [flake8 lint suppression]
1616
from const import logPath # noqa: E402 [flake8 lint suppression]
@@ -38,14 +38,14 @@ def main():
3838
mylog('verbose', [f'[{pluginName}] In script'])
3939

4040
# Retrieve configuration settings
41-
SCAN_SUBNETS = get_setting_value('SCAN_SUBNETS')
41+
scan_subnets = get_setting_value('SCAN_SUBNETS')
4242

43-
mylog('verbose', [f'[{pluginName}] SCAN_SUBNETS value: {SCAN_SUBNETS}'])
43+
parsed = parse_scan_subnets(scan_subnets)
4444

45-
# Extract interfaces from SCAN_SUBNETS
46-
interfaces = ','.join(
47-
entry.split('--interface=')[-1].strip() for entry in SCAN_SUBNETS if '--interface=' in entry
48-
)
45+
interfaces = []
46+
for entry in parsed:
47+
if entry.resolved_interface and entry.resolved_interface not in interfaces:
48+
interfaces.append(entry.resolved_interface)
4949

5050
mylog('verbose', [f'[{pluginName}] Interfaces value: "{interfaces}"'])
5151

@@ -117,27 +117,28 @@ def get_neighbors(interfaces):
117117

118118
results = []
119119

120-
for interface in interfaces.split(","):
120+
for interface in interfaces:
121121
try:
122122

123-
# Ping all IPv6 devices in multicast to trigger NDP
124-
125123
mylog('verbose', [f'[{pluginName}] Pinging on interface: "{interface}"'])
126124
command = f"ping ff02::1%{interface} -c 2".split()
127125
subprocess.run(command)
128-
mylog('verbose', [f'[{pluginName}] Pinging completed: "{interface}"'])
129-
130-
# Check the neighbourhood tables
131126

132-
mylog('verbose', [f'[{pluginName}] Scanning interface: "{interface}"'])
127+
mylog('verbose', [f'[{pluginName}] Pinging completed, now scanning interface: "{interface}"'])
128+
133129
command = f"ip neighbor show nud all dev {interface}".split()
134-
output = subprocess.check_output(command, universal_newlines=True)
130+
131+
output = subprocess.check_output(
132+
command,
133+
universal_newlines=True
134+
)
135+
135136
results += output.split("\n")
136137

137138
mylog('verbose', [f'[{pluginName}] Scanning interface succeded: "{interface}"'])
139+
138140
except subprocess.CalledProcessError as e:
139-
# An error occurred, handle it
140-
error_type = type(e).__name__ # Capture the error type
141+
error_type = type(e).__name__
141142
mylog('verbose', [f'[{pluginName}] Scanning interface failed: "{interface}" ({error_type})'])
142143

143144
return results

front/plugins/plugin_helper.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,68 @@
55
import re
66
import base64
77
import json
8+
from dataclasses import dataclass
89

910
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
1011

1112
sys.path.append(f"{INSTALL_PATH}/front/plugins")
1213
sys.path.append(f'{INSTALL_PATH}/server')
1314

14-
from logger import mylog # noqa: E402 [flake8 lint suppression]
15+
from logger import mylog, Logger # noqa: E402 [flake8 lint suppression]
1516
from utils.datetime_utils import timeNowUTC # noqa: E402 [flake8 lint suppression]
1617
from const import default_tz, fullConfPath # noqa: E402 [flake8 lint suppression]
18+
from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
19+
20+
# Make sure log level is initialized correctly
21+
Logger(get_setting_value('LOG_LEVEL'))
22+
23+
24+
# -------------------------------------------------------------------------------
25+
@dataclass
26+
class ScanSubnet:
27+
raw: str
28+
subnet: str
29+
interface: str | None = None
30+
vlan: str | None = None
31+
resolved_interface: str | None = None
32+
33+
34+
# -------------------------------------------------------------------------------
35+
def parse_scan_subnets(scan_subnets):
36+
"""Parse SCAN_SUBNETS entries into structured objects."""
37+
38+
results = []
39+
40+
for entry in scan_subnets:
41+
42+
interface_match = re.search(r'--interface=([^\s]+)', entry)
43+
interface = interface_match.group(1) if interface_match else None
44+
45+
vlan_match = re.search(r'--vlan=(\d+)', entry)
46+
vlan = vlan_match.group(1) if vlan_match else None
47+
48+
resolved_interface = interface
49+
50+
if interface and vlan:
51+
vlan_interface = f"{interface}.{vlan}"
52+
53+
if os.path.exists(f"/sys/class/net/{vlan_interface}"):
54+
resolved_interface = vlan_interface
55+
56+
subnet = re.sub(r'\s+--interface=[^\s]+', '', entry)
57+
subnet = re.sub(r'\s+--vlan=\d+', '', subnet)
58+
59+
results.append(
60+
ScanSubnet(
61+
raw=entry,
62+
subnet=subnet.strip(),
63+
interface=interface,
64+
vlan=vlan,
65+
resolved_interface=resolved_interface,
66+
)
67+
)
68+
69+
return results
1770

1871

1972
# -------------------------------------------------------------------------------

0 commit comments

Comments
 (0)