woninet 2.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- woninet-2.0.1/LICENSE +21 -0
- woninet-2.0.1/PKG-INFO +243 -0
- woninet-2.0.1/README.md +216 -0
- woninet-2.0.1/pyproject.toml +55 -0
- woninet-2.0.1/setup.cfg +4 -0
- woninet-2.0.1/tests/test_alert_events.py +151 -0
- woninet-2.0.1/tests/test_api.py +10 -0
- woninet-2.0.1/tests/test_ip_validation.py +9 -0
- woninet-2.0.1/tests/test_storage.py +24 -0
- woninet-2.0.1/woninet/__init__.py +2 -0
- woninet-2.0.1/woninet/__main__.py +4 -0
- woninet-2.0.1/woninet/core/__init__.py +2 -0
- woninet-2.0.1/woninet/core/alerts.py +251 -0
- woninet-2.0.1/woninet/core/collectors.py +303 -0
- woninet-2.0.1/woninet/core/engine.py +353 -0
- woninet-2.0.1/woninet/core/graph.py +63 -0
- woninet-2.0.1/woninet/core/manual_ip_enumerator.py +33 -0
- woninet-2.0.1/woninet/core/models.py +124 -0
- woninet-2.0.1/woninet/core/storage.py +163 -0
- woninet-2.0.1/woninet/core/subnet_enumerator.py +32 -0
- woninet-2.0.1/woninet/database/__init__.py +2 -0
- woninet-2.0.1/woninet/database/base.py +9 -0
- woninet-2.0.1/woninet/database/engine.py +44 -0
- woninet-2.0.1/woninet/database/repositories/__init__.py +2 -0
- woninet-2.0.1/woninet/database/repositories/alert_event_repository.py +69 -0
- woninet-2.0.1/woninet/database/repositories/alert_state_repository.py +81 -0
- woninet-2.0.1/woninet/database/repositories/device_repository.py +105 -0
- woninet-2.0.1/woninet/database/repositories/metric_repository.py +75 -0
- woninet-2.0.1/woninet/database/tables.py +89 -0
- woninet-2.0.1/woninet/logging.yaml +49 -0
- woninet-2.0.1/woninet/main.py +225 -0
- woninet-2.0.1/woninet/server/__init__.py +2 -0
- woninet-2.0.1/woninet/server/app.py +101 -0
- woninet-2.0.1/woninet/server/dependencies.py +25 -0
- woninet-2.0.1/woninet/server/routes/__init__.py +2 -0
- woninet-2.0.1/woninet/server/routes/api/__init__.py +0 -0
- woninet-2.0.1/woninet/server/routes/api/devices.py +43 -0
- woninet-2.0.1/woninet/server/routes/api/stats.py +34 -0
- woninet-2.0.1/woninet/server/routes/page/__init__.py +0 -0
- woninet-2.0.1/woninet/server/routes/page/devices.py +28 -0
- woninet-2.0.1/woninet/server/static/css/404.css +164 -0
- woninet-2.0.1/woninet/server/static/css/style.css +368 -0
- woninet-2.0.1/woninet/server/static/js/app.js +242 -0
- woninet-2.0.1/woninet/server/static/js/device_info.js +95 -0
- woninet-2.0.1/woninet/server/static/swagger-ui/favicon-32x32.png +0 -0
- woninet-2.0.1/woninet/server/static/swagger-ui/swagger-ui-bundle.js +2 -0
- woninet-2.0.1/woninet/server/static/swagger-ui/swagger-ui.css +3 -0
- woninet-2.0.1/woninet/server/templates/404.html +76 -0
- woninet-2.0.1/woninet/server/templates/dashboard.html +118 -0
- woninet-2.0.1/woninet/server/templates/device_info.html +93 -0
- woninet-2.0.1/woninet/utilities/__init__.py +2 -0
- woninet-2.0.1/woninet/utilities/arguments.py +94 -0
- woninet-2.0.1/woninet/utilities/cli_parser.py +90 -0
- woninet-2.0.1/woninet/utilities/detect_ip_range.py +21 -0
- woninet-2.0.1/woninet/utilities/ip_validator.py +39 -0
- woninet-2.0.1/woninet/utilities/logger.py +97 -0
- woninet-2.0.1/woninet.egg-info/PKG-INFO +243 -0
- woninet-2.0.1/woninet.egg-info/SOURCES.txt +60 -0
- woninet-2.0.1/woninet.egg-info/dependency_links.txt +1 -0
- woninet-2.0.1/woninet.egg-info/entry_points.txt +2 -0
- woninet-2.0.1/woninet.egg-info/requires.txt +11 -0
- woninet-2.0.1/woninet.egg-info/top_level.txt +1 -0
woninet-2.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alireza Teymuri
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
woninet-2.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: woninet
|
|
3
|
+
Version: 2.0.1
|
|
4
|
+
Summary: A Python-based ARP+ICMP network monitoring tool
|
|
5
|
+
Author-email: Alireza Teymuri <teymuri.alireza98@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/teymuri-alireza/woninet
|
|
8
|
+
Project-URL: Documentation, https://github.com/teymuri-alireza/woninet/tree/main/docs
|
|
9
|
+
Project-URL: Repository, https://github.com/teymuri-alireza/woninet
|
|
10
|
+
Project-URL: Issues, https://github.com/teymuri-alireza/woninet/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/teymuri-alireza/woninet/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: network,network-monitoring,icmp,arp
|
|
13
|
+
Requires-Python: >=3.12
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Requires-Dist: icmplib<3.1,>=3.0.4
|
|
17
|
+
Requires-Dist: fastapi<0.137,>=0.136.0
|
|
18
|
+
Requires-Dist: Jinja2<3.2,>=3.1.6
|
|
19
|
+
Requires-Dist: uvicorn<0.45,>=0.44.0
|
|
20
|
+
Requires-Dist: PyYAML<6.1,>=6.0.3
|
|
21
|
+
Requires-Dist: SQLAlchemy<2.1,>=2.0.46
|
|
22
|
+
Requires-Dist: matplotlib<3.12,>=3.11.0
|
|
23
|
+
Provides-Extra: test
|
|
24
|
+
Requires-Dist: pytest<9.2,>=9.1.1; extra == "test"
|
|
25
|
+
Requires-Dist: httpx<0.29,>=0.28.1; extra == "test"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# woninet
|
|
29
|
+
|
|
30
|
+
A local network monitoring system.
|
|
31
|
+
|
|
32
|
+
**woninet** continuously monitors devices on a local network using ARP
|
|
33
|
+
and ICMP probes. It enumerates candidate IP addresses in a /24 subnet,
|
|
34
|
+
determines device availability, measures latency and packet loss, stores
|
|
35
|
+
metrics in SQLite, and evaluates configurable alert rules in real time.
|
|
36
|
+
|
|
37
|
+
**Note: This script requires sudo privileges.**
|
|
38
|
+
|
|
39
|
+
## Features
|
|
40
|
+
|
|
41
|
+
- **ARP + ICMP Host Detection** (Distinguishes real devices from nonexistent
|
|
42
|
+
IPs and filters out Wi‑Fi ARP‑delay noise.)
|
|
43
|
+
|
|
44
|
+
- **Alert Engine** (Evaluates real‑time alert rules (e.g., latency thresholds))
|
|
45
|
+
|
|
46
|
+
- **Threaded Collectors**
|
|
47
|
+
|
|
48
|
+
- **SQLite database** (for devices, recorded metrics, alert states, and alert events.)
|
|
49
|
+
|
|
50
|
+
- **Positional Argument for easier access to the database from the CLI**
|
|
51
|
+
|
|
52
|
+
- **Detailed Logging**
|
|
53
|
+
|
|
54
|
+
## Future Plans
|
|
55
|
+
|
|
56
|
+
- Add ARP table caching to further reduce Wi‑Fi latency noise.
|
|
57
|
+
- Accept MAC addresses as command‑line arguments.
|
|
58
|
+
- Fetch MAC address from database for offline devices.
|
|
59
|
+
- Use ARP noise limit as device state for styling in front-end.
|
|
60
|
+
- Add CLI argument to accept target IP addresses from a file.
|
|
61
|
+
|
|
62
|
+
## Known Issues
|
|
63
|
+
|
|
64
|
+
- Monitoring Scope Limited to /24 Subnet
|
|
65
|
+
|
|
66
|
+
## How To Run
|
|
67
|
+
|
|
68
|
+
**Note:** *woninet* is under active development.
|
|
69
|
+
|
|
70
|
+
## Installation
|
|
71
|
+
|
|
72
|
+
Install from PyPI:
|
|
73
|
+
|
|
74
|
+
```shell
|
|
75
|
+
pip install woninet
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Then run:
|
|
79
|
+
|
|
80
|
+
```shell
|
|
81
|
+
woninet --help
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Installation from Source
|
|
85
|
+
|
|
86
|
+
### 1. Clone the repository
|
|
87
|
+
|
|
88
|
+
```shell
|
|
89
|
+
git clone https://github.com/teymuri-alireza/woninet
|
|
90
|
+
cd woninet
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 2. Create and activate a virtual environment:
|
|
94
|
+
|
|
95
|
+
**Note:** Create the virtual environment outside the project directory to avoid
|
|
96
|
+
`Multiple top-level packages discovered in a flat-layout` errors.
|
|
97
|
+
|
|
98
|
+
**Unix/Linux note:** The `--copies` flag forces Python to copy the interpreter
|
|
99
|
+
instead of using a symbolic link. This is required because you will apply Linux
|
|
100
|
+
capabilities directly to the Python binary in the virtual environment.
|
|
101
|
+
|
|
102
|
+
#### Linux/macOS
|
|
103
|
+
|
|
104
|
+
```shell
|
|
105
|
+
python3 -m venv --copies ../venv
|
|
106
|
+
source ../venv/bin/activate
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### Windows (PowerShell)
|
|
110
|
+
|
|
111
|
+
```shell
|
|
112
|
+
py -m venv ..\venv
|
|
113
|
+
..\venv\Scripts\Activate.ps1
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 3. Install woninet locally:
|
|
117
|
+
|
|
118
|
+
```shell
|
|
119
|
+
pip3 install .
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 4. Run woninet
|
|
123
|
+
|
|
124
|
+
**Unix/Linux only:** To allow raw ICMP without running as root, grant the required
|
|
125
|
+
capability to the Python interpreter inside the virtual environment:
|
|
126
|
+
|
|
127
|
+
```shell
|
|
128
|
+
sudo setcap cap_net_raw=eip ../venv/bin/python3
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Finally, run woninet normally:
|
|
132
|
+
|
|
133
|
+
```shell
|
|
134
|
+
woninet --version
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Alternative (not recommended):** You can run woninet with sudo instead of using
|
|
138
|
+
setcap, but this is discouraged for security reasons:
|
|
139
|
+
|
|
140
|
+
```shell
|
|
141
|
+
sudo ../venv/bin/python3 -m woninet
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Configuration
|
|
145
|
+
|
|
146
|
+
You can define alert rules in a JSON file. Rename the example file at
|
|
147
|
+
`woninet/config.example.json` to `config.json`. The application will load the alert
|
|
148
|
+
rules from that file at startup.
|
|
149
|
+
|
|
150
|
+
### Example:
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"monitoring": {
|
|
155
|
+
"arp_noise_limit": 300.0,
|
|
156
|
+
"max_workers": 4
|
|
157
|
+
},
|
|
158
|
+
"database": "woninet.db",
|
|
159
|
+
"target_ip_list": [
|
|
160
|
+
"192.168.1.1-100"
|
|
161
|
+
// or
|
|
162
|
+
"192.168.1.1", "192.168.1.10", "192.168.1.20"
|
|
163
|
+
],
|
|
164
|
+
"alert_rules":[
|
|
165
|
+
{
|
|
166
|
+
"metric": "latency",
|
|
167
|
+
"threshold": 100,
|
|
168
|
+
"consecutive_checks": 3
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"metric": "packet_loss",
|
|
172
|
+
"threshold": 0.0,
|
|
173
|
+
"consecutive_checks": 1
|
|
174
|
+
}
|
|
175
|
+
]
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Explanations:
|
|
180
|
+
|
|
181
|
+
#### Monitoring Settings
|
|
182
|
+
|
|
183
|
+
- **arp_noise_limit**: Latency threshold in milliseconds used to filter ARP
|
|
184
|
+
resolution noise. Set to 0 to disable filtering. Helps eliminate Wi‑Fi latency spikes.
|
|
185
|
+
- **max_workers**: Maximum number of thread workers used to send ICMP pings.
|
|
186
|
+
Higher values may increase system load and latency.
|
|
187
|
+
|
|
188
|
+
#### Database
|
|
189
|
+
|
|
190
|
+
- **database**: The path to the SQLite database file where metrics and alerts are stored.
|
|
191
|
+
|
|
192
|
+
#### Target IP List
|
|
193
|
+
|
|
194
|
+
- A single or range of target IP addresses to scan.
|
|
195
|
+
|
|
196
|
+
#### Alert Rules
|
|
197
|
+
|
|
198
|
+
- **metric**: The metric name to evaluate (`latency` or `packet_loss`)
|
|
199
|
+
- **threshold**: The value that triggers the alert. For latency, this is in milliseconds.
|
|
200
|
+
For packet loss, this is a percentage (0.0-100.0).
|
|
201
|
+
- **consecutive_checks**: The number of consecutive evaluations required before the alert
|
|
202
|
+
state changes. Higher values reduce false positives.
|
|
203
|
+
|
|
204
|
+
#### Notes
|
|
205
|
+
|
|
206
|
+
- **Packet Loss**: Values are in the range [0.0, 100.0]. A value of 100.0 indicates 100%
|
|
207
|
+
packet loss. A value of 0.0 will trigger on any packet loss.
|
|
208
|
+
- **Latency**: All latency thresholds are evaluated in milliseconds.
|
|
209
|
+
|
|
210
|
+
## Usage
|
|
211
|
+
|
|
212
|
+
This [guide](./docs/usage.md) explains common ways to run *woninet* and how to resolve common issues.
|
|
213
|
+
|
|
214
|
+
## Testing
|
|
215
|
+
|
|
216
|
+
If you want to run the test suite locally, install the optional test dependencies:
|
|
217
|
+
|
|
218
|
+
```shell
|
|
219
|
+
pip install -e ".[test]"
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Then run:
|
|
223
|
+
|
|
224
|
+
```shell
|
|
225
|
+
pytest
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
The test suite currently focuses on API behavior and core logic, and it uses pytest for automated verification.
|
|
229
|
+
|
|
230
|
+
## Requirements
|
|
231
|
+
|
|
232
|
+
- Python >= 3.12
|
|
233
|
+
- Root privileges
|
|
234
|
+
|
|
235
|
+
## Documentation
|
|
236
|
+
|
|
237
|
+
if you have any question or need clarification, check the [documentation](./docs/documentation.md) guide.
|
|
238
|
+
|
|
239
|
+
## Contribution
|
|
240
|
+
|
|
241
|
+
Thanks for considering contributing to *woninet!*
|
|
242
|
+
To help us maintain code quality, stability, and a predictable release process, please review
|
|
243
|
+
and follow the guidelines in our [Contributing Guide.](./CONTRIBUTING.md)
|
woninet-2.0.1/README.md
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# woninet
|
|
2
|
+
|
|
3
|
+
A local network monitoring system.
|
|
4
|
+
|
|
5
|
+
**woninet** continuously monitors devices on a local network using ARP
|
|
6
|
+
and ICMP probes. It enumerates candidate IP addresses in a /24 subnet,
|
|
7
|
+
determines device availability, measures latency and packet loss, stores
|
|
8
|
+
metrics in SQLite, and evaluates configurable alert rules in real time.
|
|
9
|
+
|
|
10
|
+
**Note: This script requires sudo privileges.**
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- **ARP + ICMP Host Detection** (Distinguishes real devices from nonexistent
|
|
15
|
+
IPs and filters out Wi‑Fi ARP‑delay noise.)
|
|
16
|
+
|
|
17
|
+
- **Alert Engine** (Evaluates real‑time alert rules (e.g., latency thresholds))
|
|
18
|
+
|
|
19
|
+
- **Threaded Collectors**
|
|
20
|
+
|
|
21
|
+
- **SQLite database** (for devices, recorded metrics, alert states, and alert events.)
|
|
22
|
+
|
|
23
|
+
- **Positional Argument for easier access to the database from the CLI**
|
|
24
|
+
|
|
25
|
+
- **Detailed Logging**
|
|
26
|
+
|
|
27
|
+
## Future Plans
|
|
28
|
+
|
|
29
|
+
- Add ARP table caching to further reduce Wi‑Fi latency noise.
|
|
30
|
+
- Accept MAC addresses as command‑line arguments.
|
|
31
|
+
- Fetch MAC address from database for offline devices.
|
|
32
|
+
- Use ARP noise limit as device state for styling in front-end.
|
|
33
|
+
- Add CLI argument to accept target IP addresses from a file.
|
|
34
|
+
|
|
35
|
+
## Known Issues
|
|
36
|
+
|
|
37
|
+
- Monitoring Scope Limited to /24 Subnet
|
|
38
|
+
|
|
39
|
+
## How To Run
|
|
40
|
+
|
|
41
|
+
**Note:** *woninet* is under active development.
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
Install from PyPI:
|
|
46
|
+
|
|
47
|
+
```shell
|
|
48
|
+
pip install woninet
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Then run:
|
|
52
|
+
|
|
53
|
+
```shell
|
|
54
|
+
woninet --help
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Installation from Source
|
|
58
|
+
|
|
59
|
+
### 1. Clone the repository
|
|
60
|
+
|
|
61
|
+
```shell
|
|
62
|
+
git clone https://github.com/teymuri-alireza/woninet
|
|
63
|
+
cd woninet
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 2. Create and activate a virtual environment:
|
|
67
|
+
|
|
68
|
+
**Note:** Create the virtual environment outside the project directory to avoid
|
|
69
|
+
`Multiple top-level packages discovered in a flat-layout` errors.
|
|
70
|
+
|
|
71
|
+
**Unix/Linux note:** The `--copies` flag forces Python to copy the interpreter
|
|
72
|
+
instead of using a symbolic link. This is required because you will apply Linux
|
|
73
|
+
capabilities directly to the Python binary in the virtual environment.
|
|
74
|
+
|
|
75
|
+
#### Linux/macOS
|
|
76
|
+
|
|
77
|
+
```shell
|
|
78
|
+
python3 -m venv --copies ../venv
|
|
79
|
+
source ../venv/bin/activate
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
#### Windows (PowerShell)
|
|
83
|
+
|
|
84
|
+
```shell
|
|
85
|
+
py -m venv ..\venv
|
|
86
|
+
..\venv\Scripts\Activate.ps1
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 3. Install woninet locally:
|
|
90
|
+
|
|
91
|
+
```shell
|
|
92
|
+
pip3 install .
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 4. Run woninet
|
|
96
|
+
|
|
97
|
+
**Unix/Linux only:** To allow raw ICMP without running as root, grant the required
|
|
98
|
+
capability to the Python interpreter inside the virtual environment:
|
|
99
|
+
|
|
100
|
+
```shell
|
|
101
|
+
sudo setcap cap_net_raw=eip ../venv/bin/python3
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Finally, run woninet normally:
|
|
105
|
+
|
|
106
|
+
```shell
|
|
107
|
+
woninet --version
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Alternative (not recommended):** You can run woninet with sudo instead of using
|
|
111
|
+
setcap, but this is discouraged for security reasons:
|
|
112
|
+
|
|
113
|
+
```shell
|
|
114
|
+
sudo ../venv/bin/python3 -m woninet
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Configuration
|
|
118
|
+
|
|
119
|
+
You can define alert rules in a JSON file. Rename the example file at
|
|
120
|
+
`woninet/config.example.json` to `config.json`. The application will load the alert
|
|
121
|
+
rules from that file at startup.
|
|
122
|
+
|
|
123
|
+
### Example:
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"monitoring": {
|
|
128
|
+
"arp_noise_limit": 300.0,
|
|
129
|
+
"max_workers": 4
|
|
130
|
+
},
|
|
131
|
+
"database": "woninet.db",
|
|
132
|
+
"target_ip_list": [
|
|
133
|
+
"192.168.1.1-100"
|
|
134
|
+
// or
|
|
135
|
+
"192.168.1.1", "192.168.1.10", "192.168.1.20"
|
|
136
|
+
],
|
|
137
|
+
"alert_rules":[
|
|
138
|
+
{
|
|
139
|
+
"metric": "latency",
|
|
140
|
+
"threshold": 100,
|
|
141
|
+
"consecutive_checks": 3
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"metric": "packet_loss",
|
|
145
|
+
"threshold": 0.0,
|
|
146
|
+
"consecutive_checks": 1
|
|
147
|
+
}
|
|
148
|
+
]
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Explanations:
|
|
153
|
+
|
|
154
|
+
#### Monitoring Settings
|
|
155
|
+
|
|
156
|
+
- **arp_noise_limit**: Latency threshold in milliseconds used to filter ARP
|
|
157
|
+
resolution noise. Set to 0 to disable filtering. Helps eliminate Wi‑Fi latency spikes.
|
|
158
|
+
- **max_workers**: Maximum number of thread workers used to send ICMP pings.
|
|
159
|
+
Higher values may increase system load and latency.
|
|
160
|
+
|
|
161
|
+
#### Database
|
|
162
|
+
|
|
163
|
+
- **database**: The path to the SQLite database file where metrics and alerts are stored.
|
|
164
|
+
|
|
165
|
+
#### Target IP List
|
|
166
|
+
|
|
167
|
+
- A single or range of target IP addresses to scan.
|
|
168
|
+
|
|
169
|
+
#### Alert Rules
|
|
170
|
+
|
|
171
|
+
- **metric**: The metric name to evaluate (`latency` or `packet_loss`)
|
|
172
|
+
- **threshold**: The value that triggers the alert. For latency, this is in milliseconds.
|
|
173
|
+
For packet loss, this is a percentage (0.0-100.0).
|
|
174
|
+
- **consecutive_checks**: The number of consecutive evaluations required before the alert
|
|
175
|
+
state changes. Higher values reduce false positives.
|
|
176
|
+
|
|
177
|
+
#### Notes
|
|
178
|
+
|
|
179
|
+
- **Packet Loss**: Values are in the range [0.0, 100.0]. A value of 100.0 indicates 100%
|
|
180
|
+
packet loss. A value of 0.0 will trigger on any packet loss.
|
|
181
|
+
- **Latency**: All latency thresholds are evaluated in milliseconds.
|
|
182
|
+
|
|
183
|
+
## Usage
|
|
184
|
+
|
|
185
|
+
This [guide](./docs/usage.md) explains common ways to run *woninet* and how to resolve common issues.
|
|
186
|
+
|
|
187
|
+
## Testing
|
|
188
|
+
|
|
189
|
+
If you want to run the test suite locally, install the optional test dependencies:
|
|
190
|
+
|
|
191
|
+
```shell
|
|
192
|
+
pip install -e ".[test]"
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Then run:
|
|
196
|
+
|
|
197
|
+
```shell
|
|
198
|
+
pytest
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
The test suite currently focuses on API behavior and core logic, and it uses pytest for automated verification.
|
|
202
|
+
|
|
203
|
+
## Requirements
|
|
204
|
+
|
|
205
|
+
- Python >= 3.12
|
|
206
|
+
- Root privileges
|
|
207
|
+
|
|
208
|
+
## Documentation
|
|
209
|
+
|
|
210
|
+
if you have any question or need clarification, check the [documentation](./docs/documentation.md) guide.
|
|
211
|
+
|
|
212
|
+
## Contribution
|
|
213
|
+
|
|
214
|
+
Thanks for considering contributing to *woninet!*
|
|
215
|
+
To help us maintain code quality, stability, and a predictable release process, please review
|
|
216
|
+
and follow the guidelines in our [Contributing Guide.](./CONTRIBUTING.md)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=80", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "woninet"
|
|
7
|
+
version = "2.0.1"
|
|
8
|
+
description = "A Python-based ARP+ICMP network monitoring tool"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "Alireza Teymuri", email = "teymuri.alireza98@gmail.com" }
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
keywords = [
|
|
18
|
+
"network",
|
|
19
|
+
"network-monitoring",
|
|
20
|
+
"icmp",
|
|
21
|
+
"arp",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
dependencies = [
|
|
25
|
+
"icmplib>=3.0.4,<3.1",
|
|
26
|
+
"fastapi>=0.136.0,<0.137",
|
|
27
|
+
"Jinja2>=3.1.6,<3.2",
|
|
28
|
+
"uvicorn>=0.44.0,<0.45",
|
|
29
|
+
"PyYAML>=6.0.3,<6.1",
|
|
30
|
+
"SQLAlchemy>=2.0.46,<2.1",
|
|
31
|
+
"matplotlib>=3.11.0,<3.12",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[tool.setuptools.package-data]
|
|
35
|
+
woninet = [
|
|
36
|
+
"server/static/**/*",
|
|
37
|
+
"server/templates/**/*",
|
|
38
|
+
"logging.yaml",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[project.urls]
|
|
42
|
+
Homepage = "https://github.com/teymuri-alireza/woninet"
|
|
43
|
+
Documentation = "https://github.com/teymuri-alireza/woninet/tree/main/docs"
|
|
44
|
+
Repository = "https://github.com/teymuri-alireza/woninet"
|
|
45
|
+
Issues = "https://github.com/teymuri-alireza/woninet/issues"
|
|
46
|
+
Changelog = "https://github.com/teymuri-alireza/woninet/blob/main/CHANGELOG.md"
|
|
47
|
+
|
|
48
|
+
[project.scripts]
|
|
49
|
+
woninet = "woninet.main:main"
|
|
50
|
+
|
|
51
|
+
[project.optional-dependencies]
|
|
52
|
+
test = [
|
|
53
|
+
"pytest>=9.1.1,<9.2",
|
|
54
|
+
"httpx>=0.28.1,<0.29",
|
|
55
|
+
]
|
woninet-2.0.1/setup.cfg
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
from woninet.core.storage import StorageEngine
|
|
2
|
+
from woninet.core.models import MetricRecord
|
|
3
|
+
from woninet.core.alerts import AlertRule, AlertEngine
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_latency_trigger_alert_event(db_session):
|
|
7
|
+
storage = StorageEngine(session_factory=lambda: db_session)
|
|
8
|
+
|
|
9
|
+
metric = MetricRecord(device_ip="192.168.1.10", metric="latency", value=200)
|
|
10
|
+
storage.store_metric(metric)
|
|
11
|
+
|
|
12
|
+
alert_rule = {"metric": "latency", "threshold": 100, "consecutive_checks": 0}
|
|
13
|
+
|
|
14
|
+
alert = AlertEngine(
|
|
15
|
+
storage=storage,
|
|
16
|
+
rules=[
|
|
17
|
+
AlertRule(
|
|
18
|
+
alert_rule["metric"],
|
|
19
|
+
alert_rule["threshold"],
|
|
20
|
+
alert_rule["consecutive_checks"],
|
|
21
|
+
)
|
|
22
|
+
],
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
alert.evaluate(
|
|
26
|
+
ip="192.168.1.10",
|
|
27
|
+
metrics_list=[
|
|
28
|
+
MetricRecord(device_ip="192.168.1.10", metric="latency", value=200),
|
|
29
|
+
],
|
|
30
|
+
default_consecutive_checks={
|
|
31
|
+
"latency": 0,
|
|
32
|
+
},
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
latency = storage.get_recent_alert_events()[0]
|
|
36
|
+
|
|
37
|
+
assert latency.event_type == "trigger"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_packet_loss_trigger_alert_event(db_session):
|
|
41
|
+
storage = StorageEngine(session_factory=lambda: db_session)
|
|
42
|
+
|
|
43
|
+
metric = MetricRecord(device_ip="192.168.1.10", metric="packet_loss", value=0.5)
|
|
44
|
+
storage.store_metric(metric)
|
|
45
|
+
|
|
46
|
+
alert_rule = {"metric": "packet_loss", "threshold": 0.0, "consecutive_checks": 0}
|
|
47
|
+
|
|
48
|
+
alert = AlertEngine(
|
|
49
|
+
storage=storage,
|
|
50
|
+
rules=[
|
|
51
|
+
AlertRule(
|
|
52
|
+
alert_rule["metric"],
|
|
53
|
+
alert_rule["threshold"],
|
|
54
|
+
alert_rule["consecutive_checks"],
|
|
55
|
+
)
|
|
56
|
+
],
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
alert.evaluate(
|
|
60
|
+
ip="192.168.1.10",
|
|
61
|
+
metrics_list=[
|
|
62
|
+
MetricRecord(device_ip="192.168.1.10", metric="packet_loss", value=0.5),
|
|
63
|
+
],
|
|
64
|
+
default_consecutive_checks={
|
|
65
|
+
"packet_loss": 0,
|
|
66
|
+
},
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
packet_loss = storage.get_recent_alert_events()[0]
|
|
70
|
+
|
|
71
|
+
assert packet_loss.event_type == "trigger"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_latency_recover_alert_event(db_session):
|
|
75
|
+
storage = StorageEngine(session_factory=lambda: db_session)
|
|
76
|
+
|
|
77
|
+
metric = MetricRecord(device_ip="192.168.1.10", metric="latency", value=20)
|
|
78
|
+
storage.store_metric(metric)
|
|
79
|
+
|
|
80
|
+
state = storage.get_or_create_alert_state(
|
|
81
|
+
ip="192.168.1.10", metric="latency", consecutive_checks=0
|
|
82
|
+
)
|
|
83
|
+
state.state = "warning"
|
|
84
|
+
storage.update_alert_state(state)
|
|
85
|
+
|
|
86
|
+
alert_rule = {"metric": "latency", "threshold": 100, "consecutive_checks": 0}
|
|
87
|
+
|
|
88
|
+
alert = AlertEngine(
|
|
89
|
+
storage=storage,
|
|
90
|
+
rules=[
|
|
91
|
+
AlertRule(
|
|
92
|
+
alert_rule["metric"],
|
|
93
|
+
alert_rule["threshold"],
|
|
94
|
+
alert_rule["consecutive_checks"],
|
|
95
|
+
)
|
|
96
|
+
],
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
alert.evaluate(
|
|
100
|
+
ip="192.168.1.10",
|
|
101
|
+
metrics_list=[
|
|
102
|
+
MetricRecord(device_ip="192.168.1.10", metric="latency", value=20),
|
|
103
|
+
],
|
|
104
|
+
default_consecutive_checks={
|
|
105
|
+
"latency": 0,
|
|
106
|
+
},
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
latency = storage.get_recent_alert_events()[0]
|
|
110
|
+
|
|
111
|
+
assert latency.event_type == "recover"
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def test_packet_loss_recover_alert_event(db_session):
|
|
115
|
+
storage = StorageEngine(session_factory=lambda: db_session)
|
|
116
|
+
|
|
117
|
+
metric = MetricRecord(device_ip="192.168.1.10", metric="packet_loss", value=0.0)
|
|
118
|
+
storage.store_metric(metric)
|
|
119
|
+
|
|
120
|
+
state = storage.get_or_create_alert_state(
|
|
121
|
+
ip="192.168.1.10", metric="packet_loss", consecutive_checks=0
|
|
122
|
+
)
|
|
123
|
+
state.state = "warning"
|
|
124
|
+
storage.update_alert_state(state)
|
|
125
|
+
|
|
126
|
+
alert_rule = {"metric": "packet_loss", "threshold": 0.0, "consecutive_checks": 0}
|
|
127
|
+
|
|
128
|
+
alert = AlertEngine(
|
|
129
|
+
storage=storage,
|
|
130
|
+
rules=[
|
|
131
|
+
AlertRule(
|
|
132
|
+
alert_rule["metric"],
|
|
133
|
+
alert_rule["threshold"],
|
|
134
|
+
alert_rule["consecutive_checks"],
|
|
135
|
+
)
|
|
136
|
+
],
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
alert.evaluate(
|
|
140
|
+
ip="192.168.1.10",
|
|
141
|
+
metrics_list=[
|
|
142
|
+
MetricRecord(device_ip="192.168.1.10", metric="packet_loss", value=0.0),
|
|
143
|
+
],
|
|
144
|
+
default_consecutive_checks={
|
|
145
|
+
"packet_loss": 0,
|
|
146
|
+
},
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
packet_loss = storage.get_recent_alert_events()[0]
|
|
150
|
+
|
|
151
|
+
assert packet_loss.event_type == "recover"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
def test_dashboard_endpoint(client):
|
|
2
|
+
response = client.get("/")
|
|
3
|
+
assert response.status_code == 200
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_devices_api_returns_mocked_devices(client):
|
|
7
|
+
response = client.get("/api/devices")
|
|
8
|
+
|
|
9
|
+
assert response.status_code == 200
|
|
10
|
+
assert response.json()["devices"][0]["ip"] == "192.168.1.10"
|