ipmg 2.3.0__tar.gz → 3.0.0__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.
- {ipmg-2.3.0/src/ipmg.egg-info → ipmg-3.0.0}/PKG-INFO +198 -20
- {ipmg-2.3.0 → ipmg-3.0.0}/README.md +187 -13
- {ipmg-2.3.0 → ipmg-3.0.0}/pyproject.toml +18 -7
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/__init__.py +1 -1
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/cli/commands.py +30 -4
- ipmg-3.0.0/src/ipmg/cli/config.py +415 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/cli/parser.py +51 -8
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/core/diff.py +8 -4
- ipmg-3.0.0/src/ipmg/core/discovery.py +254 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/core/ping.py +37 -1
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/core/portscan.py +6 -2
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/exceptions.py +4 -0
- ipmg-3.0.0/src/ipmg/infrastructure/file_io.py +529 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/infrastructure/incremental.py +57 -21
- ipmg-3.0.0/src/ipmg/reporting/frames.py +74 -0
- ipmg-3.0.0/src/ipmg/reporting/machine.py +86 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/reporting/summary.py +12 -9
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/reporting/ui.py +13 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/services/scan_service.py +76 -22
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/utils/helpers.py +10 -7
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/web/app.py +23 -52
- {ipmg-2.3.0 → ipmg-3.0.0/src/ipmg.egg-info}/PKG-INFO +198 -20
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg.egg-info/SOURCES.txt +5 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg.egg-info/requires.txt +14 -6
- {ipmg-2.3.0 → ipmg-3.0.0}/tests/test_commands.py +31 -1
- ipmg-3.0.0/tests/test_config.py +418 -0
- ipmg-3.0.0/tests/test_file_io.py +378 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/tests/test_incremental.py +15 -11
- ipmg-3.0.0/tests/test_ipv6.py +275 -0
- ipmg-3.0.0/tests/test_machine_output.py +147 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/tests/test_parser.py +37 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/tests/test_scan_service.py +55 -18
- {ipmg-2.3.0 → ipmg-3.0.0}/tests/test_web_api.py +16 -5
- ipmg-2.3.0/src/ipmg/core/discovery.py +0 -75
- ipmg-2.3.0/src/ipmg/infrastructure/file_io.py +0 -329
- ipmg-2.3.0/src/ipmg/reporting/frames.py +0 -45
- ipmg-2.3.0/tests/test_file_io.py +0 -192
- {ipmg-2.3.0 → ipmg-3.0.0}/LICENSE +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/setup.cfg +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/__main__.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/cli/__init__.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/core/__init__.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/core/engine.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/core/health.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/core/security.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/infrastructure/__init__.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/infrastructure/database.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/infrastructure/notify.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/reporting/__init__.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/reporting/diff_report.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/reporting/live.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/services/__init__.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/services/history_service.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/utils/__init__.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/web/__init__.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/web/db.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/web/manager.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/web/schemas.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/web/server.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/web/static/css/app.css +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/web/static/index.html +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/web/static/js/api.js +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/web/static/js/app.js +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/web/static/js/charts.js +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/web/static/js/demo.js +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg/web/static/js/views.js +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg.egg-info/dependency_links.txt +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg.egg-info/entry_points.txt +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/src/ipmg.egg-info/top_level.txt +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/tests/test_api_contract.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/tests/test_database_history.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/tests/test_diff.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/tests/test_diff_report.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/tests/test_discover.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/tests/test_engine.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/tests/test_health.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/tests/test_history_service.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/tests/test_live.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/tests/test_notify.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/tests/test_ping.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/tests/test_ping_command.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/tests/test_portscan.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/tests/test_ui.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/tests/test_utils.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/tests/test_web_db.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/tests/test_web_manager.py +0 -0
- {ipmg-2.3.0 → ipmg-3.0.0}/tests/test_web_server.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ipmg
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: IP Management & Ping Monitoring CLI Tool
|
|
5
5
|
Author: Sameer Alam
|
|
6
6
|
Maintainer-email: Sameer Alam <sameeralam3127@gmail.com>
|
|
@@ -29,15 +29,19 @@ Classifier: Environment :: Console
|
|
|
29
29
|
Requires-Python: >=3.9
|
|
30
30
|
Description-Content-Type: text/markdown
|
|
31
31
|
License-File: LICENSE
|
|
32
|
-
Requires-Dist: pandas>=2.2.2
|
|
33
32
|
Requires-Dist: openpyxl>=3.1
|
|
34
33
|
Requires-Dist: rich>=13.0
|
|
35
|
-
Requires-Dist:
|
|
36
|
-
|
|
37
|
-
Requires-Dist:
|
|
38
|
-
Requires-Dist:
|
|
39
|
-
Requires-Dist:
|
|
34
|
+
Requires-Dist: tomli>=2.0.1; python_version < "3.11"
|
|
35
|
+
Provides-Extra: web
|
|
36
|
+
Requires-Dist: fastapi>=0.110; extra == "web"
|
|
37
|
+
Requires-Dist: pydantic>=2.7; extra == "web"
|
|
38
|
+
Requires-Dist: uvicorn>=0.27; extra == "web"
|
|
39
|
+
Requires-Dist: websockets>=12; extra == "web"
|
|
40
|
+
Requires-Dist: python-multipart>=0.0.9; extra == "web"
|
|
41
|
+
Provides-Extra: all
|
|
42
|
+
Requires-Dist: ipmg[web]; extra == "all"
|
|
40
43
|
Provides-Extra: dev
|
|
44
|
+
Requires-Dist: ipmg[web]; extra == "dev"
|
|
41
45
|
Requires-Dist: pytest>=7.4; extra == "dev"
|
|
42
46
|
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
43
47
|
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
@@ -150,11 +154,24 @@ The formula lives in
|
|
|
150
154
|
and is updated with every release. Upgrade with `brew upgrade ipmg`, remove
|
|
151
155
|
with `brew uninstall ipmg`.
|
|
152
156
|
|
|
153
|
-
**Windows —
|
|
157
|
+
**Windows — one command in PowerShell:**
|
|
154
158
|
|
|
155
159
|
```powershell
|
|
156
|
-
|
|
157
|
-
|
|
160
|
+
irm https://raw.githubusercontent.com/sameeralam3127/ipmg/main/install.ps1 | iex
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
It works in Windows PowerShell 5.1 and PowerShell 7 and needs no
|
|
164
|
+
administrator rights: it installs uv if it is missing, installs IPMG, adds it
|
|
165
|
+
to your user `PATH`, and checks that `ipmg --version` runs. To pin a release:
|
|
166
|
+
|
|
167
|
+
```powershell
|
|
168
|
+
& ([scriptblock]::Create((irm https://raw.githubusercontent.com/sameeralam3127/ipmg/main/install.ps1))) -Version 2.3.0
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**Docker (amd64 and arm64):**
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
docker run --rm ghcr.io/sameeralam3127/ipmg --input 10.0.0.0/24
|
|
158
175
|
```
|
|
159
176
|
|
|
160
177
|
Then check it works, on any platform:
|
|
@@ -163,6 +180,38 @@ Then check it works, on any platform:
|
|
|
163
180
|
ipmg --version
|
|
164
181
|
```
|
|
165
182
|
|
|
183
|
+
### Running in Docker
|
|
184
|
+
|
|
185
|
+
The image carries `ping`, runs as an unprivileged user (uid 10001), and keeps
|
|
186
|
+
everything a scan writes (reports and the history database) in `/data`. Mount
|
|
187
|
+
a volume there to keep it between runs:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
docker run --rm -v ipmg-data:/data ghcr.io/sameeralam3127/ipmg --input 10.0.0.0/24 --compare
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Ping needs no added capability: it uses the unprivileged ICMP sockets that
|
|
194
|
+
Docker and containerd enable by default, so it also works with
|
|
195
|
+
`--cap-drop ALL` and under Kubernetes' restricted pod security profile. To
|
|
196
|
+
scan your LAN rather than the container's network, add `--network host`
|
|
197
|
+
(Linux).
|
|
198
|
+
|
|
199
|
+
For IPMG Web, use the [`compose.yaml`](https://github.com/sameeralam3127/ipmg/blob/main/compose.yaml)
|
|
200
|
+
in this repository. Inside a container IPMG Web must listen on `0.0.0.0` or
|
|
201
|
+
nothing outside the container could reach it, so the compose file starts it
|
|
202
|
+
with `web --host 0.0.0.0` and publishes the port on the host's `127.0.0.1`
|
|
203
|
+
only. Set a fixed `IPMG_WEB_TOKEN` in `.env`, then:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
docker compose up -d # http://127.0.0.1:8080/#token=<your token>
|
|
207
|
+
docker compose run --rm scan --input 10.0.0.0/24 # CLI scans land in the same history
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Publishing the port more widely (`8080:8080`) exposes IPMG Web on your
|
|
211
|
+
network over plain HTTP; put a reverse proxy with TLS in front first. Images
|
|
212
|
+
are tagged with the release version (`2.4.0`), the minor line (`2.4`), and
|
|
213
|
+
`latest`.
|
|
214
|
+
|
|
166
215
|
### Installing with pip
|
|
167
216
|
|
|
168
217
|
`pip install ipmg` works inside a virtual environment, and inside one only.
|
|
@@ -192,6 +241,22 @@ python3 -m venv ~/.venvs/ipmg
|
|
|
192
241
|
|
|
193
242
|
Please do not reach for `--break-system-packages`. It does what it says.
|
|
194
243
|
|
|
244
|
+
### What gets installed
|
|
245
|
+
|
|
246
|
+
`pip install ipmg` is the lean core: scanning, every report format
|
|
247
|
+
(Excel included), history, change detection, notifications, and exit codes.
|
|
248
|
+
It is about 9 MB and 7 packages. IPMG Web, the browser UI with its REST API
|
|
249
|
+
and Prometheus `/metrics`, is the optional `web` extra, because its server
|
|
250
|
+
stack is most of the weight:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
pip install "ipmg[web]" # or: uv tool install "ipmg[web]", pipx install "ipmg[web]"
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
The one-line installers, Homebrew, and the Docker image install it with the
|
|
257
|
+
`web` extra already. Run `ipmg web` without it and IPMG tells you the command
|
|
258
|
+
that adds it.
|
|
259
|
+
|
|
195
260
|
### The one thing IPMG needs from your system
|
|
196
261
|
|
|
197
262
|
IPMG probes hosts with your operating system's `ping` command, and minimal
|
|
@@ -331,8 +396,10 @@ Or pick a ready-made command:
|
|
|
331
396
|
| --- | --- |
|
|
332
397
|
| Scan the network I am on | `ipmg --discover` |
|
|
333
398
|
| Scan hosts listed in a file | `ipmg --input targets.txt` |
|
|
399
|
+
| Scan a file plus a few extra hosts | `ipmg --input targets.txt 10.0.0.0/30 10.0.0.5` |
|
|
334
400
|
| Get names, not just IP addresses | `ipmg --input targets.txt --resolve` |
|
|
335
401
|
| Get a report I can send to someone | `ipmg --input targets.txt --formats md csv` |
|
|
402
|
+
| Pipe the results into a script | `ipmg --input targets.txt --json \| jq .` |
|
|
336
403
|
| See hosts appear as they answer | `ipmg --input 192.168.1.0/24 --stream` |
|
|
337
404
|
| See what changed since last time | `ipmg --input targets.txt --compare` |
|
|
338
405
|
| Check which services are listening | `ipmg --input targets.txt --scan-ports` |
|
|
@@ -514,9 +581,11 @@ of it (see [Security](#security)).
|
|
|
514
581
|
|
|
515
582
|
Anywhere IPMG takes `--input`, you can give it any of these:
|
|
516
583
|
|
|
517
|
-
- **A single IP** — `8.8.8.8`
|
|
518
|
-
|
|
519
|
-
- **A
|
|
584
|
+
- **A single IP** — `8.8.8.8`, or IPv6 such as `2001:db8::1` or a link-local
|
|
585
|
+
`fe80::1%eth0`
|
|
586
|
+
- **A CIDR block** — `10.0.0.0/24`, or an IPv6 prefix up to 65,536 hosts
|
|
587
|
+
(`2001:db8::/112`)
|
|
588
|
+
- **A range** — `10.0.0.1-10.0.0.200` or `2001:db8::10-2001:db8::20`
|
|
520
589
|
- **A text file** — one IP or CIDR per line. Blank lines and `#` comments are
|
|
521
590
|
ignored, so you can annotate it:
|
|
522
591
|
|
|
@@ -526,7 +595,7 @@ Anywhere IPMG takes `--input`, you can give it any of these:
|
|
|
526
595
|
192.168.1.0/30
|
|
527
596
|
```
|
|
528
597
|
|
|
529
|
-
- **An Excel or CSV file** (`.xlsx`, `.
|
|
598
|
+
- **An Excel or CSV file** (`.xlsx`, `.csv`) — must contain a column
|
|
530
599
|
named `IP Address`. Cells can hold single addresses or CIDR blocks:
|
|
531
600
|
|
|
532
601
|
| IP Address |
|
|
@@ -534,9 +603,42 @@ Anywhere IPMG takes `--input`, you can give it any of these:
|
|
|
534
603
|
| 192.168.1.1 |
|
|
535
604
|
| 10.0.1.0/30 |
|
|
536
605
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
606
|
+
- **A JSON file** (`.json`) — a list of addresses, a list of objects keyed by
|
|
607
|
+
`IP Address`, `ip`, or `target`, or an object with a `targets` or `ips` array:
|
|
608
|
+
|
|
609
|
+
```json
|
|
610
|
+
["192.168.1.1", "10.0.1.0/30"]
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
Because `IP Address` is one of the keys it accepts, a report IPMG wrote with
|
|
614
|
+
`--formats json` can be fed straight back in:
|
|
615
|
+
|
|
616
|
+
```bash
|
|
617
|
+
ipmg --input results_20260628_120000.json
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
`--input` takes as many of these as you like, in any mix, and may also be
|
|
621
|
+
repeated:
|
|
622
|
+
|
|
623
|
+
```bash
|
|
624
|
+
ipmg --input targets.txt 10.0.0.0/30 10.0.0.5
|
|
625
|
+
ipmg --input targets.txt --input 10.0.0.5 # the same thing
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
IPv4 and IPv6 targets mix freely in one scan, and reports, history, and change
|
|
629
|
+
detection handle both. An IPv6 `/64` holds 2^64 addresses, far too many to
|
|
630
|
+
sweep one by one, so it is rejected with a pointer to `--discover ipv6`, which
|
|
631
|
+
finds the hosts on your local links through neighbour discovery instead:
|
|
632
|
+
|
|
633
|
+
```bash
|
|
634
|
+
ipmg --discover ipv6 # IPv6 neighbours on the local links
|
|
635
|
+
ipmg --discover all # the IPv4 /24 and the IPv6 neighbours together
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
Duplicate targets are removed automatically — across sources too, so a host
|
|
639
|
+
that is both in the file and on the command line is scanned once — and one scan
|
|
640
|
+
expands to at most 65,536 hosts in total. Larger CIDR blocks, ranges, or
|
|
641
|
+
combinations are rejected up front, before the scan starts.
|
|
540
642
|
|
|
541
643
|
---
|
|
542
644
|
|
|
@@ -586,6 +688,22 @@ preferring `jsonl` or `csv` (current to the last host) over `json` or `xlsx`
|
|
|
586
688
|
(current to the last autosave). Hosts dropped from the target list since are
|
|
587
689
|
left out of the finished report.
|
|
588
690
|
|
|
691
|
+
**Piping results into a script.** `--json` prints the finished scan to stdout as
|
|
692
|
+
a JSON array, and `--jsonl` prints one object per host the moment its probe
|
|
693
|
+
finishes. The human output moves to stderr, so stdout is nothing but data and no
|
|
694
|
+
temp file or glob is needed:
|
|
695
|
+
|
|
696
|
+
```bash
|
|
697
|
+
ipmg --input 10.0.0.0/24 --json | jq '.[] | select(.Status == "Active")'
|
|
698
|
+
ipmg --input 10.0.0.0/24 --jsonl | while read -r host; do notify "$host"; done
|
|
699
|
+
ipmg --input 10.0.0.0/24 --json 2>/dev/null > hosts.json # data only
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
The field names are the report columns above, and both flags print a host
|
|
703
|
+
identically — `--json` is what `--jsonl` prints, gathered into an array. Unless
|
|
704
|
+
you ask for `--formats` explicitly, a piped scan writes no report file at all.
|
|
705
|
+
Exit codes are unchanged.
|
|
706
|
+
|
|
589
707
|
**Open ports.** `Open Ports` is only populated when `--scan-ports` is set: for
|
|
590
708
|
each host that answers, IPMG probes a list of common TCP ports (SSH, HTTP,
|
|
591
709
|
HTTPS, RDP, SMB, FTP, SMTP, DNS, MSSQL, MySQL, PostgreSQL by default)
|
|
@@ -602,6 +720,61 @@ them — so piping IPMG into a file or a log gives you clean text.
|
|
|
602
720
|
|
|
603
721
|
---
|
|
604
722
|
|
|
723
|
+
## Default options in a file
|
|
724
|
+
|
|
725
|
+
If every run repeats the same flags, put them in **`ipmg.toml`** next to your
|
|
726
|
+
work instead:
|
|
727
|
+
|
|
728
|
+
```toml
|
|
729
|
+
threads = 200
|
|
730
|
+
timeout = 1
|
|
731
|
+
resolve = true
|
|
732
|
+
formats = ["md", "csv"]
|
|
733
|
+
|
|
734
|
+
[profile.datacenter]
|
|
735
|
+
threads = 400
|
|
736
|
+
scan-ports = true
|
|
737
|
+
ports = "22,80,443"
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
```bash
|
|
741
|
+
ipmg --input targets.txt # uses the file's defaults
|
|
742
|
+
ipmg --input targets.txt --profile datacenter # and the profile on top
|
|
743
|
+
ipmg --input targets.txt --threads 20 # a flag always wins
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
Any long flag can be a key, written as the flag is (`scan-ports`) or with
|
|
747
|
+
underscores (`scan_ports`). A switch takes `true` to mean "as if the flag were
|
|
748
|
+
passed" — `no-history = true` is `--no-history`. Files are read from
|
|
749
|
+
`~/.config/ipmg/config.toml` first (or `$XDG_CONFIG_HOME`), then `./ipmg.toml`
|
|
750
|
+
on top, so a project can override your global defaults:
|
|
751
|
+
|
|
752
|
+
| Flag | What it does |
|
|
753
|
+
| --- | --- |
|
|
754
|
+
| `--config PATH` | Read that file instead of searching |
|
|
755
|
+
| `--no-config` | Ignore every file and use the built-in defaults |
|
|
756
|
+
| `--profile NAME` | Apply the `[profile.NAME]` section as well |
|
|
757
|
+
|
|
758
|
+
**The command line always wins.** A flag that appears on it ignores the file
|
|
759
|
+
entirely for that flag — including `--input`, which merges several sources on
|
|
760
|
+
one command line but replaces the file's list rather than adding to it.
|
|
761
|
+
|
|
762
|
+
A key that is not a flag, a value of the wrong type, or a value outside a
|
|
763
|
+
flag's choices is an error naming the key and the file it came from:
|
|
764
|
+
|
|
765
|
+
```text
|
|
766
|
+
✗ Unknown option 'thredas' in ipmg.toml. Did you mean 'threads'?
|
|
767
|
+
✗ 'formats' in ipmg.toml: 'pdf' is not one of xlsx, csv, json, jsonl, md.
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
A project's `./ipmg.toml` comes with whatever directory you scan from,
|
|
771
|
+
including a repository you just cloned, so it cannot say where results are
|
|
772
|
+
sent: the `--notify-*` destinations and `--smtp-*` settings are only read from
|
|
773
|
+
`~/.config/ipmg/config.toml` or a file you pass with `--config`. A file also
|
|
774
|
+
cannot combine flags that exclude each other, such as `json` and `jsonl`.
|
|
775
|
+
|
|
776
|
+
---
|
|
777
|
+
|
|
605
778
|
## All options
|
|
606
779
|
|
|
607
780
|
`ipmg --help` always lists the current set. Grouped for reading:
|
|
@@ -610,10 +783,12 @@ them — so piping IPMG into a file or a log gives you clean text.
|
|
|
610
783
|
|
|
611
784
|
| Flag | Default | Description |
|
|
612
785
|
| --- | --- | --- |
|
|
613
|
-
| `--input` | `ip_list.xlsx` | What to scan:
|
|
614
|
-
| `--discover` | off |
|
|
786
|
+
| `--input` | `ip_list.xlsx` | What to scan: one or more files (`.xlsx`, `.xls`, `.csv`, `.json`, `.txt`, `.list`), IPs, CIDR blocks, or ranges (`10.0.0.1-10.0.0.50`), merged and de-duplicated |
|
|
787
|
+
| `--discover [FAMILY]` | off | Scan this machine's networks instead: `ipv4` (the default) sweeps the local /24, `ipv6` finds link neighbours, `all` does both |
|
|
615
788
|
| `--output` | `results` | Report file name prefix |
|
|
616
|
-
| `--formats` | `xlsx` | One or more of `xlsx`, `csv`, `json`, `jsonl`, `md` |
|
|
789
|
+
| `--formats` | `xlsx` | One or more of `xlsx`, `csv`, `json`, `jsonl`, `md` (no file at all with `--json`/`--jsonl`) |
|
|
790
|
+
| `--json` | off | Print the finished scan to stdout as a JSON array, human output on stderr |
|
|
791
|
+
| `--jsonl` | off | Stream one JSON object per host to stdout as each probe finishes |
|
|
617
792
|
| `--no-incremental` | off | Only write the report once the scan has finished |
|
|
618
793
|
| `--autosave` | `30` | How often a running scan re-saves `xlsx`, `json`, and `md` |
|
|
619
794
|
| `--resume` | off | Finish an interrupted scan from its partial report (newest one, or the path given) |
|
|
@@ -645,6 +820,9 @@ them — so piping IPMG into a file or a log gives you clean text.
|
|
|
645
820
|
| `--stream-all` | off | Stream every result, including hosts that did not answer (implies `--stream`) |
|
|
646
821
|
| `--stream-refresh` | `0.25` | Seconds between progress-bar redraws while streaming (0.05-5) |
|
|
647
822
|
| `--verbose` | off | Debug logging |
|
|
823
|
+
| `--config` | search | Read defaults from this file instead of searching for `ipmg.toml` |
|
|
824
|
+
| `--no-config` | off | Ignore every configuration file |
|
|
825
|
+
| `--profile` | none | Apply a `[profile.NAME]` section from the configuration file |
|
|
648
826
|
|
|
649
827
|
**Exit status**
|
|
650
828
|
|
|
@@ -102,11 +102,24 @@ The formula lives in
|
|
|
102
102
|
and is updated with every release. Upgrade with `brew upgrade ipmg`, remove
|
|
103
103
|
with `brew uninstall ipmg`.
|
|
104
104
|
|
|
105
|
-
**Windows —
|
|
105
|
+
**Windows — one command in PowerShell:**
|
|
106
106
|
|
|
107
107
|
```powershell
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
irm https://raw.githubusercontent.com/sameeralam3127/ipmg/main/install.ps1 | iex
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
It works in Windows PowerShell 5.1 and PowerShell 7 and needs no
|
|
112
|
+
administrator rights: it installs uv if it is missing, installs IPMG, adds it
|
|
113
|
+
to your user `PATH`, and checks that `ipmg --version` runs. To pin a release:
|
|
114
|
+
|
|
115
|
+
```powershell
|
|
116
|
+
& ([scriptblock]::Create((irm https://raw.githubusercontent.com/sameeralam3127/ipmg/main/install.ps1))) -Version 2.3.0
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Docker (amd64 and arm64):**
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
docker run --rm ghcr.io/sameeralam3127/ipmg --input 10.0.0.0/24
|
|
110
123
|
```
|
|
111
124
|
|
|
112
125
|
Then check it works, on any platform:
|
|
@@ -115,6 +128,38 @@ Then check it works, on any platform:
|
|
|
115
128
|
ipmg --version
|
|
116
129
|
```
|
|
117
130
|
|
|
131
|
+
### Running in Docker
|
|
132
|
+
|
|
133
|
+
The image carries `ping`, runs as an unprivileged user (uid 10001), and keeps
|
|
134
|
+
everything a scan writes (reports and the history database) in `/data`. Mount
|
|
135
|
+
a volume there to keep it between runs:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
docker run --rm -v ipmg-data:/data ghcr.io/sameeralam3127/ipmg --input 10.0.0.0/24 --compare
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Ping needs no added capability: it uses the unprivileged ICMP sockets that
|
|
142
|
+
Docker and containerd enable by default, so it also works with
|
|
143
|
+
`--cap-drop ALL` and under Kubernetes' restricted pod security profile. To
|
|
144
|
+
scan your LAN rather than the container's network, add `--network host`
|
|
145
|
+
(Linux).
|
|
146
|
+
|
|
147
|
+
For IPMG Web, use the [`compose.yaml`](https://github.com/sameeralam3127/ipmg/blob/main/compose.yaml)
|
|
148
|
+
in this repository. Inside a container IPMG Web must listen on `0.0.0.0` or
|
|
149
|
+
nothing outside the container could reach it, so the compose file starts it
|
|
150
|
+
with `web --host 0.0.0.0` and publishes the port on the host's `127.0.0.1`
|
|
151
|
+
only. Set a fixed `IPMG_WEB_TOKEN` in `.env`, then:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
docker compose up -d # http://127.0.0.1:8080/#token=<your token>
|
|
155
|
+
docker compose run --rm scan --input 10.0.0.0/24 # CLI scans land in the same history
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Publishing the port more widely (`8080:8080`) exposes IPMG Web on your
|
|
159
|
+
network over plain HTTP; put a reverse proxy with TLS in front first. Images
|
|
160
|
+
are tagged with the release version (`2.4.0`), the minor line (`2.4`), and
|
|
161
|
+
`latest`.
|
|
162
|
+
|
|
118
163
|
### Installing with pip
|
|
119
164
|
|
|
120
165
|
`pip install ipmg` works inside a virtual environment, and inside one only.
|
|
@@ -144,6 +189,22 @@ python3 -m venv ~/.venvs/ipmg
|
|
|
144
189
|
|
|
145
190
|
Please do not reach for `--break-system-packages`. It does what it says.
|
|
146
191
|
|
|
192
|
+
### What gets installed
|
|
193
|
+
|
|
194
|
+
`pip install ipmg` is the lean core: scanning, every report format
|
|
195
|
+
(Excel included), history, change detection, notifications, and exit codes.
|
|
196
|
+
It is about 9 MB and 7 packages. IPMG Web, the browser UI with its REST API
|
|
197
|
+
and Prometheus `/metrics`, is the optional `web` extra, because its server
|
|
198
|
+
stack is most of the weight:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
pip install "ipmg[web]" # or: uv tool install "ipmg[web]", pipx install "ipmg[web]"
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
The one-line installers, Homebrew, and the Docker image install it with the
|
|
205
|
+
`web` extra already. Run `ipmg web` without it and IPMG tells you the command
|
|
206
|
+
that adds it.
|
|
207
|
+
|
|
147
208
|
### The one thing IPMG needs from your system
|
|
148
209
|
|
|
149
210
|
IPMG probes hosts with your operating system's `ping` command, and minimal
|
|
@@ -283,8 +344,10 @@ Or pick a ready-made command:
|
|
|
283
344
|
| --- | --- |
|
|
284
345
|
| Scan the network I am on | `ipmg --discover` |
|
|
285
346
|
| Scan hosts listed in a file | `ipmg --input targets.txt` |
|
|
347
|
+
| Scan a file plus a few extra hosts | `ipmg --input targets.txt 10.0.0.0/30 10.0.0.5` |
|
|
286
348
|
| Get names, not just IP addresses | `ipmg --input targets.txt --resolve` |
|
|
287
349
|
| Get a report I can send to someone | `ipmg --input targets.txt --formats md csv` |
|
|
350
|
+
| Pipe the results into a script | `ipmg --input targets.txt --json \| jq .` |
|
|
288
351
|
| See hosts appear as they answer | `ipmg --input 192.168.1.0/24 --stream` |
|
|
289
352
|
| See what changed since last time | `ipmg --input targets.txt --compare` |
|
|
290
353
|
| Check which services are listening | `ipmg --input targets.txt --scan-ports` |
|
|
@@ -466,9 +529,11 @@ of it (see [Security](#security)).
|
|
|
466
529
|
|
|
467
530
|
Anywhere IPMG takes `--input`, you can give it any of these:
|
|
468
531
|
|
|
469
|
-
- **A single IP** — `8.8.8.8`
|
|
470
|
-
|
|
471
|
-
- **A
|
|
532
|
+
- **A single IP** — `8.8.8.8`, or IPv6 such as `2001:db8::1` or a link-local
|
|
533
|
+
`fe80::1%eth0`
|
|
534
|
+
- **A CIDR block** — `10.0.0.0/24`, or an IPv6 prefix up to 65,536 hosts
|
|
535
|
+
(`2001:db8::/112`)
|
|
536
|
+
- **A range** — `10.0.0.1-10.0.0.200` or `2001:db8::10-2001:db8::20`
|
|
472
537
|
- **A text file** — one IP or CIDR per line. Blank lines and `#` comments are
|
|
473
538
|
ignored, so you can annotate it:
|
|
474
539
|
|
|
@@ -478,7 +543,7 @@ Anywhere IPMG takes `--input`, you can give it any of these:
|
|
|
478
543
|
192.168.1.0/30
|
|
479
544
|
```
|
|
480
545
|
|
|
481
|
-
- **An Excel or CSV file** (`.xlsx`, `.
|
|
546
|
+
- **An Excel or CSV file** (`.xlsx`, `.csv`) — must contain a column
|
|
482
547
|
named `IP Address`. Cells can hold single addresses or CIDR blocks:
|
|
483
548
|
|
|
484
549
|
| IP Address |
|
|
@@ -486,9 +551,42 @@ Anywhere IPMG takes `--input`, you can give it any of these:
|
|
|
486
551
|
| 192.168.1.1 |
|
|
487
552
|
| 10.0.1.0/30 |
|
|
488
553
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
554
|
+
- **A JSON file** (`.json`) — a list of addresses, a list of objects keyed by
|
|
555
|
+
`IP Address`, `ip`, or `target`, or an object with a `targets` or `ips` array:
|
|
556
|
+
|
|
557
|
+
```json
|
|
558
|
+
["192.168.1.1", "10.0.1.0/30"]
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
Because `IP Address` is one of the keys it accepts, a report IPMG wrote with
|
|
562
|
+
`--formats json` can be fed straight back in:
|
|
563
|
+
|
|
564
|
+
```bash
|
|
565
|
+
ipmg --input results_20260628_120000.json
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
`--input` takes as many of these as you like, in any mix, and may also be
|
|
569
|
+
repeated:
|
|
570
|
+
|
|
571
|
+
```bash
|
|
572
|
+
ipmg --input targets.txt 10.0.0.0/30 10.0.0.5
|
|
573
|
+
ipmg --input targets.txt --input 10.0.0.5 # the same thing
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
IPv4 and IPv6 targets mix freely in one scan, and reports, history, and change
|
|
577
|
+
detection handle both. An IPv6 `/64` holds 2^64 addresses, far too many to
|
|
578
|
+
sweep one by one, so it is rejected with a pointer to `--discover ipv6`, which
|
|
579
|
+
finds the hosts on your local links through neighbour discovery instead:
|
|
580
|
+
|
|
581
|
+
```bash
|
|
582
|
+
ipmg --discover ipv6 # IPv6 neighbours on the local links
|
|
583
|
+
ipmg --discover all # the IPv4 /24 and the IPv6 neighbours together
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
Duplicate targets are removed automatically — across sources too, so a host
|
|
587
|
+
that is both in the file and on the command line is scanned once — and one scan
|
|
588
|
+
expands to at most 65,536 hosts in total. Larger CIDR blocks, ranges, or
|
|
589
|
+
combinations are rejected up front, before the scan starts.
|
|
492
590
|
|
|
493
591
|
---
|
|
494
592
|
|
|
@@ -538,6 +636,22 @@ preferring `jsonl` or `csv` (current to the last host) over `json` or `xlsx`
|
|
|
538
636
|
(current to the last autosave). Hosts dropped from the target list since are
|
|
539
637
|
left out of the finished report.
|
|
540
638
|
|
|
639
|
+
**Piping results into a script.** `--json` prints the finished scan to stdout as
|
|
640
|
+
a JSON array, and `--jsonl` prints one object per host the moment its probe
|
|
641
|
+
finishes. The human output moves to stderr, so stdout is nothing but data and no
|
|
642
|
+
temp file or glob is needed:
|
|
643
|
+
|
|
644
|
+
```bash
|
|
645
|
+
ipmg --input 10.0.0.0/24 --json | jq '.[] | select(.Status == "Active")'
|
|
646
|
+
ipmg --input 10.0.0.0/24 --jsonl | while read -r host; do notify "$host"; done
|
|
647
|
+
ipmg --input 10.0.0.0/24 --json 2>/dev/null > hosts.json # data only
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
The field names are the report columns above, and both flags print a host
|
|
651
|
+
identically — `--json` is what `--jsonl` prints, gathered into an array. Unless
|
|
652
|
+
you ask for `--formats` explicitly, a piped scan writes no report file at all.
|
|
653
|
+
Exit codes are unchanged.
|
|
654
|
+
|
|
541
655
|
**Open ports.** `Open Ports` is only populated when `--scan-ports` is set: for
|
|
542
656
|
each host that answers, IPMG probes a list of common TCP ports (SSH, HTTP,
|
|
543
657
|
HTTPS, RDP, SMB, FTP, SMTP, DNS, MSSQL, MySQL, PostgreSQL by default)
|
|
@@ -554,6 +668,61 @@ them — so piping IPMG into a file or a log gives you clean text.
|
|
|
554
668
|
|
|
555
669
|
---
|
|
556
670
|
|
|
671
|
+
## Default options in a file
|
|
672
|
+
|
|
673
|
+
If every run repeats the same flags, put them in **`ipmg.toml`** next to your
|
|
674
|
+
work instead:
|
|
675
|
+
|
|
676
|
+
```toml
|
|
677
|
+
threads = 200
|
|
678
|
+
timeout = 1
|
|
679
|
+
resolve = true
|
|
680
|
+
formats = ["md", "csv"]
|
|
681
|
+
|
|
682
|
+
[profile.datacenter]
|
|
683
|
+
threads = 400
|
|
684
|
+
scan-ports = true
|
|
685
|
+
ports = "22,80,443"
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
```bash
|
|
689
|
+
ipmg --input targets.txt # uses the file's defaults
|
|
690
|
+
ipmg --input targets.txt --profile datacenter # and the profile on top
|
|
691
|
+
ipmg --input targets.txt --threads 20 # a flag always wins
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
Any long flag can be a key, written as the flag is (`scan-ports`) or with
|
|
695
|
+
underscores (`scan_ports`). A switch takes `true` to mean "as if the flag were
|
|
696
|
+
passed" — `no-history = true` is `--no-history`. Files are read from
|
|
697
|
+
`~/.config/ipmg/config.toml` first (or `$XDG_CONFIG_HOME`), then `./ipmg.toml`
|
|
698
|
+
on top, so a project can override your global defaults:
|
|
699
|
+
|
|
700
|
+
| Flag | What it does |
|
|
701
|
+
| --- | --- |
|
|
702
|
+
| `--config PATH` | Read that file instead of searching |
|
|
703
|
+
| `--no-config` | Ignore every file and use the built-in defaults |
|
|
704
|
+
| `--profile NAME` | Apply the `[profile.NAME]` section as well |
|
|
705
|
+
|
|
706
|
+
**The command line always wins.** A flag that appears on it ignores the file
|
|
707
|
+
entirely for that flag — including `--input`, which merges several sources on
|
|
708
|
+
one command line but replaces the file's list rather than adding to it.
|
|
709
|
+
|
|
710
|
+
A key that is not a flag, a value of the wrong type, or a value outside a
|
|
711
|
+
flag's choices is an error naming the key and the file it came from:
|
|
712
|
+
|
|
713
|
+
```text
|
|
714
|
+
✗ Unknown option 'thredas' in ipmg.toml. Did you mean 'threads'?
|
|
715
|
+
✗ 'formats' in ipmg.toml: 'pdf' is not one of xlsx, csv, json, jsonl, md.
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
A project's `./ipmg.toml` comes with whatever directory you scan from,
|
|
719
|
+
including a repository you just cloned, so it cannot say where results are
|
|
720
|
+
sent: the `--notify-*` destinations and `--smtp-*` settings are only read from
|
|
721
|
+
`~/.config/ipmg/config.toml` or a file you pass with `--config`. A file also
|
|
722
|
+
cannot combine flags that exclude each other, such as `json` and `jsonl`.
|
|
723
|
+
|
|
724
|
+
---
|
|
725
|
+
|
|
557
726
|
## All options
|
|
558
727
|
|
|
559
728
|
`ipmg --help` always lists the current set. Grouped for reading:
|
|
@@ -562,10 +731,12 @@ them — so piping IPMG into a file or a log gives you clean text.
|
|
|
562
731
|
|
|
563
732
|
| Flag | Default | Description |
|
|
564
733
|
| --- | --- | --- |
|
|
565
|
-
| `--input` | `ip_list.xlsx` | What to scan:
|
|
566
|
-
| `--discover` | off |
|
|
734
|
+
| `--input` | `ip_list.xlsx` | What to scan: one or more files (`.xlsx`, `.xls`, `.csv`, `.json`, `.txt`, `.list`), IPs, CIDR blocks, or ranges (`10.0.0.1-10.0.0.50`), merged and de-duplicated |
|
|
735
|
+
| `--discover [FAMILY]` | off | Scan this machine's networks instead: `ipv4` (the default) sweeps the local /24, `ipv6` finds link neighbours, `all` does both |
|
|
567
736
|
| `--output` | `results` | Report file name prefix |
|
|
568
|
-
| `--formats` | `xlsx` | One or more of `xlsx`, `csv`, `json`, `jsonl`, `md` |
|
|
737
|
+
| `--formats` | `xlsx` | One or more of `xlsx`, `csv`, `json`, `jsonl`, `md` (no file at all with `--json`/`--jsonl`) |
|
|
738
|
+
| `--json` | off | Print the finished scan to stdout as a JSON array, human output on stderr |
|
|
739
|
+
| `--jsonl` | off | Stream one JSON object per host to stdout as each probe finishes |
|
|
569
740
|
| `--no-incremental` | off | Only write the report once the scan has finished |
|
|
570
741
|
| `--autosave` | `30` | How often a running scan re-saves `xlsx`, `json`, and `md` |
|
|
571
742
|
| `--resume` | off | Finish an interrupted scan from its partial report (newest one, or the path given) |
|
|
@@ -597,6 +768,9 @@ them — so piping IPMG into a file or a log gives you clean text.
|
|
|
597
768
|
| `--stream-all` | off | Stream every result, including hosts that did not answer (implies `--stream`) |
|
|
598
769
|
| `--stream-refresh` | `0.25` | Seconds between progress-bar redraws while streaming (0.05-5) |
|
|
599
770
|
| `--verbose` | off | Debug logging |
|
|
771
|
+
| `--config` | search | Read defaults from this file instead of searching for `ipmg.toml` |
|
|
772
|
+
| `--no-config` | off | Ignore every configuration file |
|
|
773
|
+
| `--profile` | none | Apply a `[profile.NAME]` section from the configuration file |
|
|
600
774
|
|
|
601
775
|
**Exit status**
|
|
602
776
|
|
|
@@ -13,7 +13,7 @@ build-backend = "setuptools.build_meta"
|
|
|
13
13
|
|
|
14
14
|
[project]
|
|
15
15
|
name = "ipmg"
|
|
16
|
-
version = "
|
|
16
|
+
version = "3.0.0" # Managed automatically by semantic-release
|
|
17
17
|
description = "IP Management & Ping Monitoring CLI Tool"
|
|
18
18
|
readme = "README.md"
|
|
19
19
|
requires-python = ">=3.9"
|
|
@@ -48,15 +48,14 @@ classifiers = [
|
|
|
48
48
|
"Environment :: Console",
|
|
49
49
|
]
|
|
50
50
|
|
|
51
|
+
# The core is what a scan needs: the terminal UI, Excel reports and input,
|
|
52
|
+
# and the config file parser. IPMG Web's server stack is the `web` extra.
|
|
51
53
|
dependencies = [
|
|
52
|
-
"pandas>=2.2.2",
|
|
53
54
|
"openpyxl>=3.1",
|
|
54
55
|
"rich>=13.0",
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"
|
|
58
|
-
"websockets>=12",
|
|
59
|
-
"python-multipart>=0.0.9",
|
|
56
|
+
# tomllib is in the standard library from 3.11; tomli 2.x is the same
|
|
57
|
+
# parser with the same API, so the floor must not drop below it.
|
|
58
|
+
"tomli>=2.0.1; python_version < '3.11'",
|
|
60
59
|
]
|
|
61
60
|
|
|
62
61
|
[project.urls]
|
|
@@ -67,7 +66,18 @@ Issues = "https://github.com/sameeralam3127/ipmg/issues"
|
|
|
67
66
|
Changelog = "https://github.com/sameeralam3127/ipmg/releases"
|
|
68
67
|
|
|
69
68
|
[project.optional-dependencies]
|
|
69
|
+
# IPMG Web (`ipmg web`): the browser UI, its REST API, and /metrics.
|
|
70
|
+
web = [
|
|
71
|
+
"fastapi>=0.110",
|
|
72
|
+
"pydantic>=2.7",
|
|
73
|
+
"uvicorn>=0.27",
|
|
74
|
+
"websockets>=12",
|
|
75
|
+
"python-multipart>=0.0.9",
|
|
76
|
+
]
|
|
77
|
+
# Everything; what the one-line installers and the Docker image install.
|
|
78
|
+
all = ["ipmg[web]"]
|
|
70
79
|
dev = [
|
|
80
|
+
"ipmg[web]",
|
|
71
81
|
"pytest>=7.4",
|
|
72
82
|
"pytest-cov>=5.0",
|
|
73
83
|
"ruff>=0.4",
|
|
@@ -86,6 +96,7 @@ ipmg = "ipmg.cli.commands:run"
|
|
|
86
96
|
|
|
87
97
|
[dependency-groups]
|
|
88
98
|
dev = [
|
|
99
|
+
"ipmg[web]",
|
|
89
100
|
"pytest>=7.4",
|
|
90
101
|
"pytest-cov>=5.0",
|
|
91
102
|
"ruff>=0.4",
|