ipmg 2.2.0__tar.gz → 2.4.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.2.0/src/ipmg.egg-info → ipmg-2.4.0}/PKG-INFO +157 -11
- {ipmg-2.2.0 → ipmg-2.4.0}/README.md +154 -10
- {ipmg-2.2.0 → ipmg-2.4.0}/pyproject.toml +8 -1
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/__init__.py +1 -1
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/cli/commands.py +22 -2
- ipmg-2.4.0/src/ipmg/cli/config.py +410 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/cli/parser.py +158 -7
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/core/diff.py +6 -0
- ipmg-2.4.0/src/ipmg/core/health.py +64 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/exceptions.py +8 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/infrastructure/file_io.py +91 -5
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/infrastructure/incremental.py +10 -0
- ipmg-2.4.0/src/ipmg/infrastructure/notify.py +403 -0
- ipmg-2.4.0/src/ipmg/reporting/machine.py +88 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/reporting/ui.py +13 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/services/scan_service.py +96 -17
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/utils/helpers.py +10 -7
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/web/app.py +229 -69
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/web/manager.py +18 -16
- ipmg-2.4.0/src/ipmg/web/schemas.py +239 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/web/server.py +1 -1
- {ipmg-2.2.0 → ipmg-2.4.0/src/ipmg.egg-info}/PKG-INFO +157 -11
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg.egg-info/SOURCES.txt +10 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg.egg-info/requires.txt +4 -0
- ipmg-2.4.0/tests/test_api_contract.py +153 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/tests/test_commands.py +51 -1
- ipmg-2.4.0/tests/test_config.py +402 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/tests/test_file_io.py +146 -2
- ipmg-2.4.0/tests/test_health.py +63 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/tests/test_incremental.py +8 -7
- ipmg-2.4.0/tests/test_machine_output.py +147 -0
- ipmg-2.4.0/tests/test_notify.py +520 -0
- ipmg-2.4.0/tests/test_parser.py +157 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/tests/test_scan_service.py +184 -10
- {ipmg-2.2.0 → ipmg-2.4.0}/tests/test_web_api.py +64 -0
- ipmg-2.2.0/tests/test_parser.py +0 -67
- {ipmg-2.2.0 → ipmg-2.4.0}/LICENSE +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/setup.cfg +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/__main__.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/cli/__init__.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/core/__init__.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/core/discovery.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/core/engine.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/core/ping.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/core/portscan.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/core/security.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/infrastructure/__init__.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/infrastructure/database.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/reporting/__init__.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/reporting/diff_report.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/reporting/frames.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/reporting/live.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/reporting/summary.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/services/__init__.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/services/history_service.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/utils/__init__.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/web/__init__.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/web/db.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/web/static/css/app.css +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/web/static/index.html +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/web/static/js/api.js +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/web/static/js/app.js +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/web/static/js/charts.js +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/web/static/js/demo.js +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg/web/static/js/views.js +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg.egg-info/dependency_links.txt +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg.egg-info/entry_points.txt +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/src/ipmg.egg-info/top_level.txt +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/tests/test_database_history.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/tests/test_diff.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/tests/test_diff_report.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/tests/test_discover.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/tests/test_engine.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/tests/test_history_service.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/tests/test_live.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/tests/test_ping.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/tests/test_ping_command.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/tests/test_portscan.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/tests/test_ui.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/tests/test_utils.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/tests/test_web_db.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/tests/test_web_manager.py +0 -0
- {ipmg-2.2.0 → ipmg-2.4.0}/tests/test_web_server.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ipmg
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: IP Management & Ping Monitoring CLI Tool
|
|
5
5
|
Author: Sameer Alam
|
|
6
6
|
Maintainer-email: Sameer Alam <sameeralam3127@gmail.com>
|
|
@@ -33,9 +33,11 @@ Requires-Dist: pandas>=2.2.2
|
|
|
33
33
|
Requires-Dist: openpyxl>=3.1
|
|
34
34
|
Requires-Dist: rich>=13.0
|
|
35
35
|
Requires-Dist: fastapi>=0.110
|
|
36
|
+
Requires-Dist: pydantic>=2.7
|
|
36
37
|
Requires-Dist: uvicorn>=0.27
|
|
37
38
|
Requires-Dist: websockets>=12
|
|
38
39
|
Requires-Dist: python-multipart>=0.0.9
|
|
40
|
+
Requires-Dist: tomli>=2.0.1; python_version < "3.11"
|
|
39
41
|
Provides-Extra: dev
|
|
40
42
|
Requires-Dist: pytest>=7.4; extra == "dev"
|
|
41
43
|
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
@@ -330,8 +332,10 @@ Or pick a ready-made command:
|
|
|
330
332
|
| --- | --- |
|
|
331
333
|
| Scan the network I am on | `ipmg --discover` |
|
|
332
334
|
| Scan hosts listed in a file | `ipmg --input targets.txt` |
|
|
335
|
+
| Scan a file plus a few extra hosts | `ipmg --input targets.txt 10.0.0.0/30 10.0.0.5` |
|
|
333
336
|
| Get names, not just IP addresses | `ipmg --input targets.txt --resolve` |
|
|
334
337
|
| Get a report I can send to someone | `ipmg --input targets.txt --formats md csv` |
|
|
338
|
+
| Pipe the results into a script | `ipmg --input targets.txt --json \| jq .` |
|
|
335
339
|
| See hosts appear as they answer | `ipmg --input 192.168.1.0/24 --stream` |
|
|
336
340
|
| See what changed since last time | `ipmg --input targets.txt --compare` |
|
|
337
341
|
| Check which services are listening | `ipmg --input targets.txt --scan-ports` |
|
|
@@ -390,6 +394,21 @@ ipmg diff --fail-on-change # exit 2 when anything changed (CI)
|
|
|
390
394
|
ipmg history --limit 10 # list stored scans
|
|
391
395
|
```
|
|
392
396
|
|
|
397
|
+
To hear about changes without watching the terminal, send them to Slack,
|
|
398
|
+
Microsoft Teams, any JSON webhook, or email. Any `--notify-*` flag turns on
|
|
399
|
+
`--compare`, and with `--interval` every pass that changes something alerts:
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
ipmg --input targets.txt --interval 15 --notify-slack # URL from $IPMG_NOTIFY_SLACK
|
|
403
|
+
ipmg --input targets.txt --notify-email ops@example.com --smtp-host smtp.example.com
|
|
404
|
+
ipmg diff --notify-webhook https://ops.example.com/ipmg --notify-severity critical
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
Only changes at or above `--notify-severity` (default `warning`) trigger a
|
|
408
|
+
notification. A notification that fails is reported but never fails the scan.
|
|
409
|
+
Secrets such as webhook URLs and `IPMG_SMTP_PASSWORD` can come from environment
|
|
410
|
+
variables; see [Notifications](docs/COMMANDS.md#notifications) for all of them.
|
|
411
|
+
|
|
393
412
|
What counts as a change:
|
|
394
413
|
|
|
395
414
|
| Change | Severity | Meaning |
|
|
@@ -422,6 +441,11 @@ source**, so file-based and `--discover` runs do not get mixed up. Pass
|
|
|
422
441
|
| `--latency-threshold` | `5` | Minimum latency delta in ms |
|
|
423
442
|
| `--latency-pct` | `25` | Minimum relative latency change |
|
|
424
443
|
| `--fail-on-change` | off | `ipmg diff` exits 2 when changes are found |
|
|
444
|
+
| `--notify-webhook` | off | POST the change report as JSON to a URL |
|
|
445
|
+
| `--notify-slack` | off | Post changes to a Slack incoming webhook |
|
|
446
|
+
| `--notify-teams` | off | Post changes to a Microsoft Teams Workflows webhook |
|
|
447
|
+
| `--notify-email` | off | Email the Markdown change report (with `--smtp-host`, `--smtp-port`, `--smtp-security`, `--smtp-user`, `--smtp-from`) |
|
|
448
|
+
| `--notify-severity` | `warning` | Only notify for changes at least this severe |
|
|
425
449
|
|
|
426
450
|
---
|
|
427
451
|
|
|
@@ -462,6 +486,14 @@ package, nothing is loaded from a CDN. It gives you:
|
|
|
462
486
|
| `--no-browser` | off | Don't open the browser automatically |
|
|
463
487
|
| `--db` | `~/.ipmg/dashboard.db` | History database location |
|
|
464
488
|
|
|
489
|
+
### Scripting IPMG Web
|
|
490
|
+
|
|
491
|
+
Everything the dashboard does goes through a documented, versioned REST API
|
|
492
|
+
under `/api/v1`, so you can start scans, fetch results, and compare scans from
|
|
493
|
+
your own scripts. The [API guide](https://github.com/sameeralam3127/ipmg/blob/main/docs/API.md)
|
|
494
|
+
covers authentication, errors, and worked `curl` examples; the running server
|
|
495
|
+
also serves interactive docs at `http://127.0.0.1:8080/docs`.
|
|
496
|
+
|
|
465
497
|
### On a server with no browser
|
|
466
498
|
|
|
467
499
|
On a Linux server with no display (e.g. accessed over plain SSH), IPMG detects
|
|
@@ -505,9 +537,32 @@ Anywhere IPMG takes `--input`, you can give it any of these:
|
|
|
505
537
|
| 192.168.1.1 |
|
|
506
538
|
| 10.0.1.0/30 |
|
|
507
539
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
540
|
+
- **A JSON file** (`.json`) — a list of addresses, a list of objects keyed by
|
|
541
|
+
`IP Address`, `ip`, or `target`, or an object with a `targets` or `ips` array:
|
|
542
|
+
|
|
543
|
+
```json
|
|
544
|
+
["192.168.1.1", "10.0.1.0/30"]
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
Because `IP Address` is one of the keys it accepts, a report IPMG wrote with
|
|
548
|
+
`--formats json` can be fed straight back in:
|
|
549
|
+
|
|
550
|
+
```bash
|
|
551
|
+
ipmg --input results_20260628_120000.json
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
`--input` takes as many of these as you like, in any mix, and may also be
|
|
555
|
+
repeated:
|
|
556
|
+
|
|
557
|
+
```bash
|
|
558
|
+
ipmg --input targets.txt 10.0.0.0/30 10.0.0.5
|
|
559
|
+
ipmg --input targets.txt --input 10.0.0.5 # the same thing
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
Duplicate targets are removed automatically — across sources too, so a host
|
|
563
|
+
that is both in the file and on the command line is scanned once — and one scan
|
|
564
|
+
expands to at most 65,536 hosts in total. Larger CIDR blocks, ranges, or
|
|
565
|
+
combinations are rejected up front, before the scan starts.
|
|
511
566
|
|
|
512
567
|
---
|
|
513
568
|
|
|
@@ -557,6 +612,22 @@ preferring `jsonl` or `csv` (current to the last host) over `json` or `xlsx`
|
|
|
557
612
|
(current to the last autosave). Hosts dropped from the target list since are
|
|
558
613
|
left out of the finished report.
|
|
559
614
|
|
|
615
|
+
**Piping results into a script.** `--json` prints the finished scan to stdout as
|
|
616
|
+
a JSON array, and `--jsonl` prints one object per host the moment its probe
|
|
617
|
+
finishes. The human output moves to stderr, so stdout is nothing but data and no
|
|
618
|
+
temp file or glob is needed:
|
|
619
|
+
|
|
620
|
+
```bash
|
|
621
|
+
ipmg --input 10.0.0.0/24 --json | jq '.[] | select(.Status == "Active")'
|
|
622
|
+
ipmg --input 10.0.0.0/24 --jsonl | while read -r host; do notify "$host"; done
|
|
623
|
+
ipmg --input 10.0.0.0/24 --json 2>/dev/null > hosts.json # data only
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
The field names are the report columns above, and both flags print a host
|
|
627
|
+
identically — `--json` is what `--jsonl` prints, gathered into an array. Unless
|
|
628
|
+
you ask for `--formats` explicitly, a piped scan writes no report file at all.
|
|
629
|
+
Exit codes are unchanged.
|
|
630
|
+
|
|
560
631
|
**Open ports.** `Open Ports` is only populated when `--scan-ports` is set: for
|
|
561
632
|
each host that answers, IPMG probes a list of common TCP ports (SSH, HTTP,
|
|
562
633
|
HTTPS, RDP, SMB, FTP, SMTP, DNS, MSSQL, MySQL, PostgreSQL by default)
|
|
@@ -573,6 +644,61 @@ them — so piping IPMG into a file or a log gives you clean text.
|
|
|
573
644
|
|
|
574
645
|
---
|
|
575
646
|
|
|
647
|
+
## Default options in a file
|
|
648
|
+
|
|
649
|
+
If every run repeats the same flags, put them in **`ipmg.toml`** next to your
|
|
650
|
+
work instead:
|
|
651
|
+
|
|
652
|
+
```toml
|
|
653
|
+
threads = 200
|
|
654
|
+
timeout = 1
|
|
655
|
+
resolve = true
|
|
656
|
+
formats = ["md", "csv"]
|
|
657
|
+
|
|
658
|
+
[profile.datacenter]
|
|
659
|
+
threads = 400
|
|
660
|
+
scan-ports = true
|
|
661
|
+
ports = "22,80,443"
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
```bash
|
|
665
|
+
ipmg --input targets.txt # uses the file's defaults
|
|
666
|
+
ipmg --input targets.txt --profile datacenter # and the profile on top
|
|
667
|
+
ipmg --input targets.txt --threads 20 # a flag always wins
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
Any long flag can be a key, written as the flag is (`scan-ports`) or with
|
|
671
|
+
underscores (`scan_ports`). A switch takes `true` to mean "as if the flag were
|
|
672
|
+
passed" — `no-history = true` is `--no-history`. Files are read from
|
|
673
|
+
`~/.config/ipmg/config.toml` first (or `$XDG_CONFIG_HOME`), then `./ipmg.toml`
|
|
674
|
+
on top, so a project can override your global defaults:
|
|
675
|
+
|
|
676
|
+
| Flag | What it does |
|
|
677
|
+
| --- | --- |
|
|
678
|
+
| `--config PATH` | Read that file instead of searching |
|
|
679
|
+
| `--no-config` | Ignore every file and use the built-in defaults |
|
|
680
|
+
| `--profile NAME` | Apply the `[profile.NAME]` section as well |
|
|
681
|
+
|
|
682
|
+
**The command line always wins.** A flag that appears on it ignores the file
|
|
683
|
+
entirely for that flag — including `--input`, which merges several sources on
|
|
684
|
+
one command line but replaces the file's list rather than adding to it.
|
|
685
|
+
|
|
686
|
+
A key that is not a flag, a value of the wrong type, or a value outside a
|
|
687
|
+
flag's choices is an error naming the key and the file it came from:
|
|
688
|
+
|
|
689
|
+
```text
|
|
690
|
+
✗ Unknown option 'thredas' in ipmg.toml. Did you mean 'threads'?
|
|
691
|
+
✗ 'formats' in ipmg.toml: 'pdf' is not one of xlsx, csv, json, jsonl, md.
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
A project's `./ipmg.toml` comes with whatever directory you scan from,
|
|
695
|
+
including a repository you just cloned, so it cannot say where results are
|
|
696
|
+
sent: the `--notify-*` destinations and `--smtp-*` settings are only read from
|
|
697
|
+
`~/.config/ipmg/config.toml` or a file you pass with `--config`. A file also
|
|
698
|
+
cannot combine flags that exclude each other, such as `json` and `jsonl`.
|
|
699
|
+
|
|
700
|
+
---
|
|
701
|
+
|
|
576
702
|
## All options
|
|
577
703
|
|
|
578
704
|
`ipmg --help` always lists the current set. Grouped for reading:
|
|
@@ -581,10 +707,12 @@ them — so piping IPMG into a file or a log gives you clean text.
|
|
|
581
707
|
|
|
582
708
|
| Flag | Default | Description |
|
|
583
709
|
| --- | --- | --- |
|
|
584
|
-
| `--input` | `ip_list.xlsx` | What to scan:
|
|
710
|
+
| `--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 |
|
|
585
711
|
| `--discover` | off | Auto-detect and scan the local subnet instead |
|
|
586
712
|
| `--output` | `results` | Report file name prefix |
|
|
587
|
-
| `--formats` | `xlsx` | One or more of `xlsx`, `csv`, `json`, `jsonl`, `md` |
|
|
713
|
+
| `--formats` | `xlsx` | One or more of `xlsx`, `csv`, `json`, `jsonl`, `md` (no file at all with `--json`/`--jsonl`) |
|
|
714
|
+
| `--json` | off | Print the finished scan to stdout as a JSON array, human output on stderr |
|
|
715
|
+
| `--jsonl` | off | Stream one JSON object per host to stdout as each probe finishes |
|
|
588
716
|
| `--no-incremental` | off | Only write the report once the scan has finished |
|
|
589
717
|
| `--autosave` | `30` | How often a running scan re-saves `xlsx`, `json`, and `md` |
|
|
590
718
|
| `--resume` | off | Finish an interrupted scan from its partial report (newest one, or the path given) |
|
|
@@ -616,13 +744,25 @@ them — so piping IPMG into a file or a log gives you clean text.
|
|
|
616
744
|
| `--stream-all` | off | Stream every result, including hosts that did not answer (implies `--stream`) |
|
|
617
745
|
| `--stream-refresh` | `0.25` | Seconds between progress-bar redraws while streaming (0.05-5) |
|
|
618
746
|
| `--verbose` | off | Debug logging |
|
|
747
|
+
| `--config` | search | Read defaults from this file instead of searching for `ipmg.toml` |
|
|
748
|
+
| `--no-config` | off | Ignore every configuration file |
|
|
749
|
+
| `--profile` | none | Apply a `[profile.NAME]` section from the configuration file |
|
|
750
|
+
|
|
751
|
+
**Exit status**
|
|
752
|
+
|
|
753
|
+
| Flag | Default | Description |
|
|
754
|
+
| --- | --- | --- |
|
|
755
|
+
| `--fail-on-down` | off | Exit 3 if any target is not `Active` (reports are still written) |
|
|
756
|
+
| `--min-active` | off | Exit 3 if fewer than this percentage of targets are `Active` |
|
|
619
757
|
|
|
620
758
|
**History and changes** — see [Change detection](#change-detection) for
|
|
621
759
|
`--compare`, `--no-history`, `--db`, `--diff-formats`, `--diff-output`,
|
|
622
|
-
`--latency-threshold`, `--latency-pct`,
|
|
760
|
+
`--latency-threshold`, `--latency-pct`, `--fail-on-change`, and the
|
|
761
|
+
`--notify-*` flags.
|
|
623
762
|
|
|
624
763
|
Exit codes: `0` success, `1` error, `2` changes detected
|
|
625
|
-
(`ipmg diff --fail-on-change`), `
|
|
764
|
+
(`ipmg diff --fail-on-change`), `3` hosts down (`--fail-on-down`,
|
|
765
|
+
`--min-active`), `130` interrupted.
|
|
626
766
|
|
|
627
767
|
---
|
|
628
768
|
|
|
@@ -643,12 +783,15 @@ itself is hardened accordingly:
|
|
|
643
783
|
- Uploads are capped at 5 MB and one scan expands to at most 65,536 hosts,
|
|
644
784
|
so a bad input file cannot exhaust memory
|
|
645
785
|
- All database access uses parameterized SQL
|
|
786
|
+
- The only outbound requests IPMG makes are the notifications you ask for
|
|
787
|
+
with a `--notify-*` flag. Their URLs and the SMTP password can come from
|
|
788
|
+
environment variables, and error messages never repeat them
|
|
646
789
|
|
|
647
790
|
If you bind to a non-local address with `--host`, the token still guards the
|
|
648
791
|
API. It travels over plain HTTP, though, so use an SSH tunnel or a reverse
|
|
649
792
|
proxy with TLS on any network you don't trust.
|
|
650
793
|
|
|
651
|
-
Found a vulnerability? See [SECURITY.md](SECURITY.md) for how to report it.
|
|
794
|
+
Found a vulnerability? See [SECURITY.md](.github/SECURITY.md) for how to report it.
|
|
652
795
|
|
|
653
796
|
---
|
|
654
797
|
|
|
@@ -657,6 +800,9 @@ Found a vulnerability? See [SECURITY.md](SECURITY.md) for how to report it.
|
|
|
657
800
|
- **[Command reference](https://github.com/sameeralam3127/ipmg/blob/main/docs/COMMANDS.md)** —
|
|
658
801
|
every command and flag with copy-paste examples, a safe session that tries
|
|
659
802
|
everything on your own machine, exit codes, and error messages
|
|
803
|
+
- **[Web API guide](https://github.com/sameeralam3127/ipmg/blob/main/docs/API.md)** —
|
|
804
|
+
script IPMG Web over HTTP: start scans, fetch results, compare scans,
|
|
805
|
+
and follow live events
|
|
660
806
|
- **[Troubleshooting](https://github.com/sameeralam3127/ipmg/blob/main/docs/TROUBLESHOOTING.md)** —
|
|
661
807
|
install errors, `command not found`, every host timing out, slow scans,
|
|
662
808
|
rejected input files
|
|
@@ -670,11 +816,11 @@ Found a vulnerability? See [SECURITY.md](SECURITY.md) for how to report it.
|
|
|
670
816
|
## Contributing
|
|
671
817
|
|
|
672
818
|
Contributions are welcome.
|
|
673
|
-
[CONTRIBUTING.md](https://github.com/sameeralam3127/ipmg/blob/main/CONTRIBUTING.md)
|
|
819
|
+
[CONTRIBUTING.md](https://github.com/sameeralam3127/ipmg/blob/main/.github/CONTRIBUTING.md)
|
|
674
820
|
covers setting up a development environment, running the tests, the commit
|
|
675
821
|
message format that drives automated releases, and how the project website and
|
|
676
822
|
IPMG Web demo are built. Everyone taking part is expected to follow the
|
|
677
|
-
[Code of Conduct](https://github.com/sameeralam3127/ipmg/blob/main/CODE_OF_CONDUCT.md).
|
|
823
|
+
[Code of Conduct](https://github.com/sameeralam3127/ipmg/blob/main/.github/CODE_OF_CONDUCT.md).
|
|
678
824
|
|
|
679
825
|
---
|
|
680
826
|
|
|
@@ -283,8 +283,10 @@ Or pick a ready-made command:
|
|
|
283
283
|
| --- | --- |
|
|
284
284
|
| Scan the network I am on | `ipmg --discover` |
|
|
285
285
|
| Scan hosts listed in a file | `ipmg --input targets.txt` |
|
|
286
|
+
| Scan a file plus a few extra hosts | `ipmg --input targets.txt 10.0.0.0/30 10.0.0.5` |
|
|
286
287
|
| Get names, not just IP addresses | `ipmg --input targets.txt --resolve` |
|
|
287
288
|
| Get a report I can send to someone | `ipmg --input targets.txt --formats md csv` |
|
|
289
|
+
| Pipe the results into a script | `ipmg --input targets.txt --json \| jq .` |
|
|
288
290
|
| See hosts appear as they answer | `ipmg --input 192.168.1.0/24 --stream` |
|
|
289
291
|
| See what changed since last time | `ipmg --input targets.txt --compare` |
|
|
290
292
|
| Check which services are listening | `ipmg --input targets.txt --scan-ports` |
|
|
@@ -343,6 +345,21 @@ ipmg diff --fail-on-change # exit 2 when anything changed (CI)
|
|
|
343
345
|
ipmg history --limit 10 # list stored scans
|
|
344
346
|
```
|
|
345
347
|
|
|
348
|
+
To hear about changes without watching the terminal, send them to Slack,
|
|
349
|
+
Microsoft Teams, any JSON webhook, or email. Any `--notify-*` flag turns on
|
|
350
|
+
`--compare`, and with `--interval` every pass that changes something alerts:
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
ipmg --input targets.txt --interval 15 --notify-slack # URL from $IPMG_NOTIFY_SLACK
|
|
354
|
+
ipmg --input targets.txt --notify-email ops@example.com --smtp-host smtp.example.com
|
|
355
|
+
ipmg diff --notify-webhook https://ops.example.com/ipmg --notify-severity critical
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Only changes at or above `--notify-severity` (default `warning`) trigger a
|
|
359
|
+
notification. A notification that fails is reported but never fails the scan.
|
|
360
|
+
Secrets such as webhook URLs and `IPMG_SMTP_PASSWORD` can come from environment
|
|
361
|
+
variables; see [Notifications](docs/COMMANDS.md#notifications) for all of them.
|
|
362
|
+
|
|
346
363
|
What counts as a change:
|
|
347
364
|
|
|
348
365
|
| Change | Severity | Meaning |
|
|
@@ -375,6 +392,11 @@ source**, so file-based and `--discover` runs do not get mixed up. Pass
|
|
|
375
392
|
| `--latency-threshold` | `5` | Minimum latency delta in ms |
|
|
376
393
|
| `--latency-pct` | `25` | Minimum relative latency change |
|
|
377
394
|
| `--fail-on-change` | off | `ipmg diff` exits 2 when changes are found |
|
|
395
|
+
| `--notify-webhook` | off | POST the change report as JSON to a URL |
|
|
396
|
+
| `--notify-slack` | off | Post changes to a Slack incoming webhook |
|
|
397
|
+
| `--notify-teams` | off | Post changes to a Microsoft Teams Workflows webhook |
|
|
398
|
+
| `--notify-email` | off | Email the Markdown change report (with `--smtp-host`, `--smtp-port`, `--smtp-security`, `--smtp-user`, `--smtp-from`) |
|
|
399
|
+
| `--notify-severity` | `warning` | Only notify for changes at least this severe |
|
|
378
400
|
|
|
379
401
|
---
|
|
380
402
|
|
|
@@ -415,6 +437,14 @@ package, nothing is loaded from a CDN. It gives you:
|
|
|
415
437
|
| `--no-browser` | off | Don't open the browser automatically |
|
|
416
438
|
| `--db` | `~/.ipmg/dashboard.db` | History database location |
|
|
417
439
|
|
|
440
|
+
### Scripting IPMG Web
|
|
441
|
+
|
|
442
|
+
Everything the dashboard does goes through a documented, versioned REST API
|
|
443
|
+
under `/api/v1`, so you can start scans, fetch results, and compare scans from
|
|
444
|
+
your own scripts. The [API guide](https://github.com/sameeralam3127/ipmg/blob/main/docs/API.md)
|
|
445
|
+
covers authentication, errors, and worked `curl` examples; the running server
|
|
446
|
+
also serves interactive docs at `http://127.0.0.1:8080/docs`.
|
|
447
|
+
|
|
418
448
|
### On a server with no browser
|
|
419
449
|
|
|
420
450
|
On a Linux server with no display (e.g. accessed over plain SSH), IPMG detects
|
|
@@ -458,9 +488,32 @@ Anywhere IPMG takes `--input`, you can give it any of these:
|
|
|
458
488
|
| 192.168.1.1 |
|
|
459
489
|
| 10.0.1.0/30 |
|
|
460
490
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
491
|
+
- **A JSON file** (`.json`) — a list of addresses, a list of objects keyed by
|
|
492
|
+
`IP Address`, `ip`, or `target`, or an object with a `targets` or `ips` array:
|
|
493
|
+
|
|
494
|
+
```json
|
|
495
|
+
["192.168.1.1", "10.0.1.0/30"]
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
Because `IP Address` is one of the keys it accepts, a report IPMG wrote with
|
|
499
|
+
`--formats json` can be fed straight back in:
|
|
500
|
+
|
|
501
|
+
```bash
|
|
502
|
+
ipmg --input results_20260628_120000.json
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
`--input` takes as many of these as you like, in any mix, and may also be
|
|
506
|
+
repeated:
|
|
507
|
+
|
|
508
|
+
```bash
|
|
509
|
+
ipmg --input targets.txt 10.0.0.0/30 10.0.0.5
|
|
510
|
+
ipmg --input targets.txt --input 10.0.0.5 # the same thing
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
Duplicate targets are removed automatically — across sources too, so a host
|
|
514
|
+
that is both in the file and on the command line is scanned once — and one scan
|
|
515
|
+
expands to at most 65,536 hosts in total. Larger CIDR blocks, ranges, or
|
|
516
|
+
combinations are rejected up front, before the scan starts.
|
|
464
517
|
|
|
465
518
|
---
|
|
466
519
|
|
|
@@ -510,6 +563,22 @@ preferring `jsonl` or `csv` (current to the last host) over `json` or `xlsx`
|
|
|
510
563
|
(current to the last autosave). Hosts dropped from the target list since are
|
|
511
564
|
left out of the finished report.
|
|
512
565
|
|
|
566
|
+
**Piping results into a script.** `--json` prints the finished scan to stdout as
|
|
567
|
+
a JSON array, and `--jsonl` prints one object per host the moment its probe
|
|
568
|
+
finishes. The human output moves to stderr, so stdout is nothing but data and no
|
|
569
|
+
temp file or glob is needed:
|
|
570
|
+
|
|
571
|
+
```bash
|
|
572
|
+
ipmg --input 10.0.0.0/24 --json | jq '.[] | select(.Status == "Active")'
|
|
573
|
+
ipmg --input 10.0.0.0/24 --jsonl | while read -r host; do notify "$host"; done
|
|
574
|
+
ipmg --input 10.0.0.0/24 --json 2>/dev/null > hosts.json # data only
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
The field names are the report columns above, and both flags print a host
|
|
578
|
+
identically — `--json` is what `--jsonl` prints, gathered into an array. Unless
|
|
579
|
+
you ask for `--formats` explicitly, a piped scan writes no report file at all.
|
|
580
|
+
Exit codes are unchanged.
|
|
581
|
+
|
|
513
582
|
**Open ports.** `Open Ports` is only populated when `--scan-ports` is set: for
|
|
514
583
|
each host that answers, IPMG probes a list of common TCP ports (SSH, HTTP,
|
|
515
584
|
HTTPS, RDP, SMB, FTP, SMTP, DNS, MSSQL, MySQL, PostgreSQL by default)
|
|
@@ -526,6 +595,61 @@ them — so piping IPMG into a file or a log gives you clean text.
|
|
|
526
595
|
|
|
527
596
|
---
|
|
528
597
|
|
|
598
|
+
## Default options in a file
|
|
599
|
+
|
|
600
|
+
If every run repeats the same flags, put them in **`ipmg.toml`** next to your
|
|
601
|
+
work instead:
|
|
602
|
+
|
|
603
|
+
```toml
|
|
604
|
+
threads = 200
|
|
605
|
+
timeout = 1
|
|
606
|
+
resolve = true
|
|
607
|
+
formats = ["md", "csv"]
|
|
608
|
+
|
|
609
|
+
[profile.datacenter]
|
|
610
|
+
threads = 400
|
|
611
|
+
scan-ports = true
|
|
612
|
+
ports = "22,80,443"
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
```bash
|
|
616
|
+
ipmg --input targets.txt # uses the file's defaults
|
|
617
|
+
ipmg --input targets.txt --profile datacenter # and the profile on top
|
|
618
|
+
ipmg --input targets.txt --threads 20 # a flag always wins
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
Any long flag can be a key, written as the flag is (`scan-ports`) or with
|
|
622
|
+
underscores (`scan_ports`). A switch takes `true` to mean "as if the flag were
|
|
623
|
+
passed" — `no-history = true` is `--no-history`. Files are read from
|
|
624
|
+
`~/.config/ipmg/config.toml` first (or `$XDG_CONFIG_HOME`), then `./ipmg.toml`
|
|
625
|
+
on top, so a project can override your global defaults:
|
|
626
|
+
|
|
627
|
+
| Flag | What it does |
|
|
628
|
+
| --- | --- |
|
|
629
|
+
| `--config PATH` | Read that file instead of searching |
|
|
630
|
+
| `--no-config` | Ignore every file and use the built-in defaults |
|
|
631
|
+
| `--profile NAME` | Apply the `[profile.NAME]` section as well |
|
|
632
|
+
|
|
633
|
+
**The command line always wins.** A flag that appears on it ignores the file
|
|
634
|
+
entirely for that flag — including `--input`, which merges several sources on
|
|
635
|
+
one command line but replaces the file's list rather than adding to it.
|
|
636
|
+
|
|
637
|
+
A key that is not a flag, a value of the wrong type, or a value outside a
|
|
638
|
+
flag's choices is an error naming the key and the file it came from:
|
|
639
|
+
|
|
640
|
+
```text
|
|
641
|
+
✗ Unknown option 'thredas' in ipmg.toml. Did you mean 'threads'?
|
|
642
|
+
✗ 'formats' in ipmg.toml: 'pdf' is not one of xlsx, csv, json, jsonl, md.
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
A project's `./ipmg.toml` comes with whatever directory you scan from,
|
|
646
|
+
including a repository you just cloned, so it cannot say where results are
|
|
647
|
+
sent: the `--notify-*` destinations and `--smtp-*` settings are only read from
|
|
648
|
+
`~/.config/ipmg/config.toml` or a file you pass with `--config`. A file also
|
|
649
|
+
cannot combine flags that exclude each other, such as `json` and `jsonl`.
|
|
650
|
+
|
|
651
|
+
---
|
|
652
|
+
|
|
529
653
|
## All options
|
|
530
654
|
|
|
531
655
|
`ipmg --help` always lists the current set. Grouped for reading:
|
|
@@ -534,10 +658,12 @@ them — so piping IPMG into a file or a log gives you clean text.
|
|
|
534
658
|
|
|
535
659
|
| Flag | Default | Description |
|
|
536
660
|
| --- | --- | --- |
|
|
537
|
-
| `--input` | `ip_list.xlsx` | What to scan:
|
|
661
|
+
| `--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 |
|
|
538
662
|
| `--discover` | off | Auto-detect and scan the local subnet instead |
|
|
539
663
|
| `--output` | `results` | Report file name prefix |
|
|
540
|
-
| `--formats` | `xlsx` | One or more of `xlsx`, `csv`, `json`, `jsonl`, `md` |
|
|
664
|
+
| `--formats` | `xlsx` | One or more of `xlsx`, `csv`, `json`, `jsonl`, `md` (no file at all with `--json`/`--jsonl`) |
|
|
665
|
+
| `--json` | off | Print the finished scan to stdout as a JSON array, human output on stderr |
|
|
666
|
+
| `--jsonl` | off | Stream one JSON object per host to stdout as each probe finishes |
|
|
541
667
|
| `--no-incremental` | off | Only write the report once the scan has finished |
|
|
542
668
|
| `--autosave` | `30` | How often a running scan re-saves `xlsx`, `json`, and `md` |
|
|
543
669
|
| `--resume` | off | Finish an interrupted scan from its partial report (newest one, or the path given) |
|
|
@@ -569,13 +695,25 @@ them — so piping IPMG into a file or a log gives you clean text.
|
|
|
569
695
|
| `--stream-all` | off | Stream every result, including hosts that did not answer (implies `--stream`) |
|
|
570
696
|
| `--stream-refresh` | `0.25` | Seconds between progress-bar redraws while streaming (0.05-5) |
|
|
571
697
|
| `--verbose` | off | Debug logging |
|
|
698
|
+
| `--config` | search | Read defaults from this file instead of searching for `ipmg.toml` |
|
|
699
|
+
| `--no-config` | off | Ignore every configuration file |
|
|
700
|
+
| `--profile` | none | Apply a `[profile.NAME]` section from the configuration file |
|
|
701
|
+
|
|
702
|
+
**Exit status**
|
|
703
|
+
|
|
704
|
+
| Flag | Default | Description |
|
|
705
|
+
| --- | --- | --- |
|
|
706
|
+
| `--fail-on-down` | off | Exit 3 if any target is not `Active` (reports are still written) |
|
|
707
|
+
| `--min-active` | off | Exit 3 if fewer than this percentage of targets are `Active` |
|
|
572
708
|
|
|
573
709
|
**History and changes** — see [Change detection](#change-detection) for
|
|
574
710
|
`--compare`, `--no-history`, `--db`, `--diff-formats`, `--diff-output`,
|
|
575
|
-
`--latency-threshold`, `--latency-pct`,
|
|
711
|
+
`--latency-threshold`, `--latency-pct`, `--fail-on-change`, and the
|
|
712
|
+
`--notify-*` flags.
|
|
576
713
|
|
|
577
714
|
Exit codes: `0` success, `1` error, `2` changes detected
|
|
578
|
-
(`ipmg diff --fail-on-change`), `
|
|
715
|
+
(`ipmg diff --fail-on-change`), `3` hosts down (`--fail-on-down`,
|
|
716
|
+
`--min-active`), `130` interrupted.
|
|
579
717
|
|
|
580
718
|
---
|
|
581
719
|
|
|
@@ -596,12 +734,15 @@ itself is hardened accordingly:
|
|
|
596
734
|
- Uploads are capped at 5 MB and one scan expands to at most 65,536 hosts,
|
|
597
735
|
so a bad input file cannot exhaust memory
|
|
598
736
|
- All database access uses parameterized SQL
|
|
737
|
+
- The only outbound requests IPMG makes are the notifications you ask for
|
|
738
|
+
with a `--notify-*` flag. Their URLs and the SMTP password can come from
|
|
739
|
+
environment variables, and error messages never repeat them
|
|
599
740
|
|
|
600
741
|
If you bind to a non-local address with `--host`, the token still guards the
|
|
601
742
|
API. It travels over plain HTTP, though, so use an SSH tunnel or a reverse
|
|
602
743
|
proxy with TLS on any network you don't trust.
|
|
603
744
|
|
|
604
|
-
Found a vulnerability? See [SECURITY.md](SECURITY.md) for how to report it.
|
|
745
|
+
Found a vulnerability? See [SECURITY.md](.github/SECURITY.md) for how to report it.
|
|
605
746
|
|
|
606
747
|
---
|
|
607
748
|
|
|
@@ -610,6 +751,9 @@ Found a vulnerability? See [SECURITY.md](SECURITY.md) for how to report it.
|
|
|
610
751
|
- **[Command reference](https://github.com/sameeralam3127/ipmg/blob/main/docs/COMMANDS.md)** —
|
|
611
752
|
every command and flag with copy-paste examples, a safe session that tries
|
|
612
753
|
everything on your own machine, exit codes, and error messages
|
|
754
|
+
- **[Web API guide](https://github.com/sameeralam3127/ipmg/blob/main/docs/API.md)** —
|
|
755
|
+
script IPMG Web over HTTP: start scans, fetch results, compare scans,
|
|
756
|
+
and follow live events
|
|
613
757
|
- **[Troubleshooting](https://github.com/sameeralam3127/ipmg/blob/main/docs/TROUBLESHOOTING.md)** —
|
|
614
758
|
install errors, `command not found`, every host timing out, slow scans,
|
|
615
759
|
rejected input files
|
|
@@ -623,11 +767,11 @@ Found a vulnerability? See [SECURITY.md](SECURITY.md) for how to report it.
|
|
|
623
767
|
## Contributing
|
|
624
768
|
|
|
625
769
|
Contributions are welcome.
|
|
626
|
-
[CONTRIBUTING.md](https://github.com/sameeralam3127/ipmg/blob/main/CONTRIBUTING.md)
|
|
770
|
+
[CONTRIBUTING.md](https://github.com/sameeralam3127/ipmg/blob/main/.github/CONTRIBUTING.md)
|
|
627
771
|
covers setting up a development environment, running the tests, the commit
|
|
628
772
|
message format that drives automated releases, and how the project website and
|
|
629
773
|
IPMG Web demo are built. Everyone taking part is expected to follow the
|
|
630
|
-
[Code of Conduct](https://github.com/sameeralam3127/ipmg/blob/main/CODE_OF_CONDUCT.md).
|
|
774
|
+
[Code of Conduct](https://github.com/sameeralam3127/ipmg/blob/main/.github/CODE_OF_CONDUCT.md).
|
|
631
775
|
|
|
632
776
|
---
|
|
633
777
|
|
|
@@ -13,7 +13,7 @@ build-backend = "setuptools.build_meta"
|
|
|
13
13
|
|
|
14
14
|
[project]
|
|
15
15
|
name = "ipmg"
|
|
16
|
-
version = "2.
|
|
16
|
+
version = "2.4.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"
|
|
@@ -53,9 +53,13 @@ dependencies = [
|
|
|
53
53
|
"openpyxl>=3.1",
|
|
54
54
|
"rich>=13.0",
|
|
55
55
|
"fastapi>=0.110",
|
|
56
|
+
"pydantic>=2.7",
|
|
56
57
|
"uvicorn>=0.27",
|
|
57
58
|
"websockets>=12",
|
|
58
59
|
"python-multipart>=0.0.9",
|
|
60
|
+
# tomllib is in the standard library from 3.11; tomli 2.x is the same
|
|
61
|
+
# parser with the same API, so the floor must not drop below it.
|
|
62
|
+
"tomli>=2.0.1; python_version < '3.11'",
|
|
59
63
|
]
|
|
60
64
|
|
|
61
65
|
[project.urls]
|
|
@@ -109,6 +113,9 @@ commit_message = "chore(release): {version}"
|
|
|
109
113
|
build_command = "python -m build"
|
|
110
114
|
major_on_zero = false
|
|
111
115
|
|
|
116
|
+
[tool.semantic_release.changelog.default_templates]
|
|
117
|
+
changelog_file = ".github/CHANGELOG.md"
|
|
118
|
+
|
|
112
119
|
|
|
113
120
|
# ==========================================================
|
|
114
121
|
# Setuptools (src layout)
|
|
@@ -6,6 +6,7 @@ import logging
|
|
|
6
6
|
import sys
|
|
7
7
|
from typing import Callable, Dict, List, Optional
|
|
8
8
|
|
|
9
|
+
from ipmg.cli import config
|
|
9
10
|
from ipmg.cli.parser import (
|
|
10
11
|
build_diff_parser,
|
|
11
12
|
build_history_parser,
|
|
@@ -13,26 +14,37 @@ from ipmg.cli.parser import (
|
|
|
13
14
|
build_web_parser,
|
|
14
15
|
)
|
|
15
16
|
from ipmg.core.diff import DiffOptions
|
|
17
|
+
from ipmg.core.health import HostsDownError
|
|
16
18
|
from ipmg.core.security import print_disclaimer_once
|
|
17
19
|
from ipmg.exceptions import IPMGError
|
|
20
|
+
from ipmg.infrastructure.notify import notify_options, send_notifications
|
|
18
21
|
from ipmg.reporting import ui
|
|
19
22
|
from ipmg.reporting.diff_report import export_diff, print_diff
|
|
20
23
|
from ipmg.reporting.summary import print_scan_history
|
|
21
24
|
from ipmg.services.history_service import HistoryService
|
|
22
|
-
from ipmg.services.scan_service import run_scan
|
|
25
|
+
from ipmg.services.scan_service import machine_output, run_scan
|
|
23
26
|
from ipmg.utils.helpers import configure_logging
|
|
24
27
|
|
|
25
28
|
EXIT_OK = 0
|
|
26
29
|
EXIT_ERROR = 1
|
|
27
30
|
EXIT_CHANGES_DETECTED = 2
|
|
31
|
+
EXIT_HOSTS_DOWN = 3
|
|
28
32
|
EXIT_INTERRUPTED = 130
|
|
29
33
|
|
|
30
34
|
log = logging.getLogger(__name__)
|
|
31
35
|
|
|
32
36
|
|
|
33
37
|
def _scan_command(argv: List[str]) -> int:
|
|
34
|
-
|
|
38
|
+
parser = build_parser()
|
|
39
|
+
# Before parsing: the file supplies the defaults, so anything on the
|
|
40
|
+
# command line still overrides it.
|
|
41
|
+
config.apply(parser, build_parser, argv)
|
|
42
|
+
args = parser.parse_args(argv)
|
|
35
43
|
configure_logging(args.verbose)
|
|
44
|
+
if machine_output(args).enabled:
|
|
45
|
+
# Before the banner: with --json or --jsonl stdout carries the results
|
|
46
|
+
# and nothing else, so every line a person reads goes to stderr.
|
|
47
|
+
ui.use_stderr()
|
|
36
48
|
ui.header("scan")
|
|
37
49
|
print_disclaimer_once()
|
|
38
50
|
run_scan(args)
|
|
@@ -77,6 +89,7 @@ def _diff_command(argv: List[str]) -> int:
|
|
|
77
89
|
ui.error("Provide at most two scan ids: BASELINE TARGET.")
|
|
78
90
|
return EXIT_ERROR
|
|
79
91
|
|
|
92
|
+
notify = notify_options(args)
|
|
80
93
|
history = HistoryService.open(args.db)
|
|
81
94
|
options = DiffOptions(
|
|
82
95
|
latency_abs_ms=max(args.latency_threshold, 0.0),
|
|
@@ -93,6 +106,7 @@ def _diff_command(argv: List[str]) -> int:
|
|
|
93
106
|
print_diff(diff, limit=args.limit)
|
|
94
107
|
if args.diff_formats:
|
|
95
108
|
export_diff(diff, args.diff_output, args.diff_formats)
|
|
109
|
+
send_notifications(diff, notify)
|
|
96
110
|
|
|
97
111
|
if args.fail_on_change and diff.has_changes:
|
|
98
112
|
return EXIT_CHANGES_DETECTED
|
|
@@ -134,6 +148,12 @@ def run(argv: Optional[List[str]] = None) -> int:
|
|
|
134
148
|
|
|
135
149
|
try:
|
|
136
150
|
return handler(handler_argv)
|
|
151
|
+
except HostsDownError as exc:
|
|
152
|
+
# Not an error: the scan finished and wrote its reports, but failed
|
|
153
|
+
# the check it was asked to make.
|
|
154
|
+
ui.blank()
|
|
155
|
+
ui.warn(str(exc))
|
|
156
|
+
return EXIT_HOSTS_DOWN
|
|
137
157
|
except IPMGError as exc:
|
|
138
158
|
ui.blank()
|
|
139
159
|
ui.error(str(exc))
|