ipmg 3.1.0__tar.gz → 3.1.2__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.
Files changed (85) hide show
  1. {ipmg-3.1.0/src/ipmg.egg-info → ipmg-3.1.2}/PKG-INFO +131 -86
  2. {ipmg-3.1.0 → ipmg-3.1.2}/README.md +130 -85
  3. {ipmg-3.1.0 → ipmg-3.1.2}/pyproject.toml +1 -1
  4. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/__init__.py +1 -1
  5. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/core/diff.py +3 -1
  6. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/core/ping.py +7 -1
  7. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/infrastructure/notify.py +21 -0
  8. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/web/static/js/demo.js +10 -2
  9. {ipmg-3.1.0 → ipmg-3.1.2/src/ipmg.egg-info}/PKG-INFO +131 -86
  10. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_diff.py +3 -0
  11. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_notify.py +50 -0
  12. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_ping.py +50 -1
  13. {ipmg-3.1.0 → ipmg-3.1.2}/LICENSE +0 -0
  14. {ipmg-3.1.0 → ipmg-3.1.2}/setup.cfg +0 -0
  15. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/__main__.py +0 -0
  16. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/cli/__init__.py +0 -0
  17. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/cli/commands.py +0 -0
  18. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/cli/config.py +0 -0
  19. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/cli/parser.py +0 -0
  20. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/core/__init__.py +0 -0
  21. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/core/discovery.py +0 -0
  22. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/core/engine.py +0 -0
  23. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/core/health.py +0 -0
  24. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/core/portscan.py +0 -0
  25. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/core/security.py +0 -0
  26. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/exceptions.py +0 -0
  27. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/infrastructure/__init__.py +0 -0
  28. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/infrastructure/database.py +0 -0
  29. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/infrastructure/file_io.py +0 -0
  30. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/infrastructure/incremental.py +0 -0
  31. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/reporting/__init__.py +0 -0
  32. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/reporting/diff_report.py +0 -0
  33. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/reporting/frames.py +0 -0
  34. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/reporting/live.py +0 -0
  35. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/reporting/machine.py +0 -0
  36. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/reporting/metrics.py +0 -0
  37. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/reporting/summary.py +0 -0
  38. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/reporting/ui.py +0 -0
  39. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/services/__init__.py +0 -0
  40. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/services/history_service.py +0 -0
  41. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/services/scan_service.py +0 -0
  42. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/utils/__init__.py +0 -0
  43. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/utils/helpers.py +0 -0
  44. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/web/__init__.py +0 -0
  45. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/web/app.py +0 -0
  46. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/web/db.py +0 -0
  47. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/web/manager.py +0 -0
  48. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/web/schemas.py +0 -0
  49. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/web/server.py +0 -0
  50. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/web/static/css/app.css +0 -0
  51. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/web/static/index.html +0 -0
  52. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/web/static/js/api.js +0 -0
  53. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/web/static/js/app.js +0 -0
  54. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/web/static/js/charts.js +0 -0
  55. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg/web/static/js/views.js +0 -0
  56. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg.egg-info/SOURCES.txt +0 -0
  57. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg.egg-info/dependency_links.txt +0 -0
  58. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg.egg-info/entry_points.txt +0 -0
  59. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg.egg-info/requires.txt +0 -0
  60. {ipmg-3.1.0 → ipmg-3.1.2}/src/ipmg.egg-info/top_level.txt +0 -0
  61. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_api_contract.py +0 -0
  62. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_commands.py +0 -0
  63. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_config.py +0 -0
  64. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_database_history.py +0 -0
  65. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_diff_report.py +0 -0
  66. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_discover.py +0 -0
  67. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_engine.py +0 -0
  68. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_file_io.py +0 -0
  69. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_health.py +0 -0
  70. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_history_service.py +0 -0
  71. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_incremental.py +0 -0
  72. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_ipv6.py +0 -0
  73. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_live.py +0 -0
  74. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_machine_output.py +0 -0
  75. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_metrics.py +0 -0
  76. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_parser.py +0 -0
  77. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_ping_command.py +0 -0
  78. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_portscan.py +0 -0
  79. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_scan_service.py +0 -0
  80. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_ui.py +0 -0
  81. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_utils.py +0 -0
  82. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_web_api.py +0 -0
  83. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_web_db.py +0 -0
  84. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_web_manager.py +0 -0
  85. {ipmg-3.1.0 → ipmg-3.1.2}/tests/test_web_server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ipmg
3
- Version: 3.1.0
3
+ Version: 3.1.2
4
4
  Summary: IP Management & Ping Monitoring CLI Tool
5
5
  Author: Sameer Alam
6
6
  Maintainer-email: Sameer Alam <sameeralam3127@gmail.com>
@@ -57,22 +57,21 @@ Dynamic: license-file
57
57
  ![License](https://img.shields.io/badge/license-MIT-green)
58
58
  [![Publish](https://github.com/sameeralam3127/ipmg/actions/workflows/publish.yml/badge.svg)](https://github.com/sameeralam3127/ipmg/actions/workflows/publish.yml)
59
59
 
60
- **Find out which hosts on your network are up — and what changed since last time.**
60
+ **Know what changed on your network since the last scan.**
61
61
 
62
- IPMG pings hosts in parallel, resolves their names, and hands you a report you
63
- can send to someone: Excel, CSV, JSON, or Markdown. It works from the command
64
- line or from IPMG Web, a local browser UI, and it remembers every scan so it can tell
65
- you what moved.
62
+ IPMG keeps a history of every scan and tells you what moved between two of
63
+ them: hosts that went offline or appeared, IP addresses and hostnames that
64
+ moved, latency that shifted. Every change carries a severity, so you can send
65
+ only the ones that matter to Slack, Teams, a webhook, or email, or fail a cron
66
+ job or CI step with an exit code. Around that: parallel ping scanning of IPs,
67
+ CIDR blocks, and ranges (IPv4 and IPv6), IPMG Web, a local dashboard that
68
+ works offline, and reports in Excel, CSV, JSON, and Markdown.
66
69
 
67
70
  <p align="center">
68
- <img src="https://raw.githubusercontent.com/sameeralam3127/ipmg/main/docs/assets/ipmg-demo.gif" alt="ipmg scanning 13 hosts in parallel: live results with reverse DNS names and latency, then a summary of 10 active and 3 timed out" width="820">
71
+ <img src="https://raw.githubusercontent.com/sameeralam3127/ipmg/main/docs/assets/ipmg-compare.gif" alt="ipmg scans 13 hosts, a new address is added to the target file, and a second scan with --compare reports a new host (warning) and a latency change (info) against the first" width="820">
69
72
  </p>
70
73
 
71
- **Why not `nmap -sn`, `fping`, or Angry IP Scanner?** They tell you what is up
72
- right now. IPMG also remembers every scan and tells you what changed since the
73
- last one: the host that dropped off, the device that appeared, the latency that
74
- doubled. It writes the report you would otherwise build by hand.
75
- [How it compares](#how-it-compares)
74
+ [How it compares](#how-it-compares) to `nmap -sn`, `fping`, and Angry IP Scanner.
76
75
 
77
76
  **Website:** [sameeralam3127.github.io/ipmg](https://sameeralam3127.github.io/ipmg/) ·
78
77
  **Live demo:** [IPMG Web with sample data](https://sameeralam3127.github.io/ipmg/demo/) ·
@@ -80,7 +79,8 @@ doubled. It writes the report you would otherwise build by hand.
80
79
 
81
80
  ```bash
82
81
  pip install ipmg
83
- ipmg --discover # scan the network you are on, right now
82
+ ipmg --discover # scan the network you are on, right now
83
+ ipmg --discover --compare # later: scan again and see what changed
84
84
  ```
85
85
 
86
86
  > **Please read:** only scan networks you are authorized to scan. Unauthorized
@@ -89,11 +89,12 @@ ipmg --discover # scan the network you are on, right now
89
89
  **Contents**
90
90
 
91
91
  [How it compares](#how-it-compares) ·
92
+ [Requirements and platform support](#requirements-and-platform-support) ·
92
93
  [Install](#install) ·
93
94
  [Your first scan](#your-first-scan) ·
95
+ [Change detection](#change-detection) ·
94
96
  [Common tasks](#common-tasks) ·
95
97
  [Live results](#live-results) ·
96
- [Change detection](#change-detection) ·
97
98
  [IPMG Web](#ipmg-web) ·
98
99
  [What you can scan](#what-you-can-scan) ·
99
100
  [Reports](#reports) ·
@@ -108,15 +109,22 @@ ipmg --discover # scan the network you are on, right now
108
109
 
109
110
  | | IPMG | `nmap -sn` | `fping` | Angry IP Scanner |
110
111
  | --- | --- | --- | --- | --- |
111
- | Parallel ping sweep | Yes | Yes | Yes | Yes |
112
+ | Parallel ping sweep | Yes, one system `ping` per host | Yes | Yes, fastest of the four | Yes |
112
113
  | Scan history and "what changed" | Built in | Save XML, compare with `ndiff` | No | No |
114
+ | Change severities and alerts | critical / warning / info; Slack, Teams, webhook, email | No | No | No |
115
+ | Exit codes for cron and CI | `3` with `--fail-on-down`, `2` with `diff --fail-on-change` | No | Non-zero if any host is unreachable | No (GUI first) |
113
116
  | Reports | Excel, CSV, JSON, Markdown | XML, grepable text | Plain text | CSV, TXT, XML |
114
- | Browser UI | IPMG Web, local | No (Zenmap is a desktop app) | No | Desktop app (Java) |
115
- | Port checks | Common TCP ports | Full port and OS scanner | No | Via fetchers |
117
+ | Browser UI | IPMG Web, local, works offline | No (Zenmap is a desktop app) | No | Desktop app (Java) |
118
+ | Port checks | Common TCP ports (connect only) | Full port, service, and OS scanner | No | Via fetchers |
116
119
 
117
- Reach for nmap when you need a real port or OS scanner. Reach for IPMG when you
118
- look after a network and need to know what moved since yesterday, with a report
119
- you can hand to someone.
120
+ Where the others are better: **nmap** is a real port, service, and OS
121
+ scanner, and IPMG does not try to be one. **fping** sends every probe from a
122
+ single process, so on large ranges it is much faster than IPMG, which starts
123
+ one `ping` per host (`--threads` at a time). Angry IP Scanner is a
124
+ point-and-click desktop app with a plugin system.
125
+
126
+ Reach for IPMG when you look after a network and need to know what moved since
127
+ yesterday, with a report you can hand to someone.
120
128
 
121
129
  <p align="center">
122
130
  <img src="https://raw.githubusercontent.com/sameeralam3127/ipmg/main/docs/assets/ipmg-web.png" alt="IPMG Web dashboard: scan totals, a status donut of 16 active, 1 timeout and 1 inactive host, a latency trend chart, and a list of recent scans" width="820">
@@ -124,6 +132,32 @@ you can hand to someone.
124
132
 
125
133
  ---
126
134
 
135
+ ## Requirements and platform support
136
+
137
+ IPMG does not craft packets. It runs your operating system's `ping` command
138
+ once per host, as a direct process call with no shell, and reads its output.
139
+ That means:
140
+
141
+ - **Python 3.9 or newer**, unless you use the one-line installers, which
142
+ bring their own.
143
+ - **The system `ping` command.** macOS and Windows include it; minimal Linux
144
+ and container images may not ([how to install it](#the-one-thing-ipmg-needs-from-your-system)).
145
+ - **No root or administrator rights.** IPMG has exactly the privileges your
146
+ `ping` has: if `ping 8.8.8.8` works for your user, a scan works too. Port
147
+ checks (`--scan-ports`) are ordinary TCP connections.
148
+
149
+ | Platform | What IPMG runs | Status |
150
+ | --- | --- | --- |
151
+ | Linux (iputils or busybox `ping`) | `ping -c COUNT -W SECONDS` | Supported. CI runs the tests and a scan on Ubuntu; see [verified environments](#verified-environments) |
152
+ | macOS | `ping -c COUNT -W MILLISECONDS`; `ping6` for IPv6 | Supported. CI runs the tests and a scan on macOS |
153
+ | Windows | `ping -n COUNT -w MILLISECONDS` | Supported. CI runs the tests and a scan of `127.0.0.1` on `windows-latest`; not yet verified against a real LAN |
154
+ | FreeBSD, OpenBSD, NetBSD | the macOS flags | Untested. The code treats them like macOS, and their `ping` options may differ |
155
+
156
+ `--discover ipv6` reads the neighbour table with `ip -6 neigh` on Linux (the
157
+ `iproute2` package), `ndp -an` on macOS, and `netsh` on Windows.
158
+
159
+ ---
160
+
127
161
  ## Install
128
162
 
129
163
  **Linux and macOS — one command, works on every distribution:**
@@ -377,71 +411,6 @@ exist yet. A file you name with `--input` must already exist.
377
411
 
378
412
  ---
379
413
 
380
- ## Common tasks
381
-
382
- Not sure which flags you need? The
383
- [command builder](https://sameeralam3127.github.io/ipmg/#builder) on the
384
- website puts the command together as you pick what you want to know, and
385
- explains every flag it adds.
386
-
387
- <p align="center">
388
- <a href="https://sameeralam3127.github.io/ipmg/#builder">
389
- <img src="https://raw.githubusercontent.com/sameeralam3127/ipmg/main/docs/assets/ipmg-builder.png" alt="IPMG command builder: choose Scan, Compare, History, or Web, enter a target such as 192.168.1.0/24, switch on options like live results or hostnames, and copy the generated ipmg command with each flag explained" width="820">
390
- </a>
391
- </p>
392
-
393
- Or pick a ready-made command:
394
-
395
- | I want to… | Command |
396
- | --- | --- |
397
- | Scan the network I am on | `ipmg --discover` |
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` |
400
- | Get names, not just IP addresses | `ipmg --input targets.txt --resolve` |
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 .` |
403
- | See hosts appear as they answer | `ipmg --input 192.168.1.0/24 --stream` |
404
- | See what changed since last time | `ipmg --input targets.txt --compare` |
405
- | Check which services are listening | `ipmg --input targets.txt --scan-ports` |
406
- | Keep scanning every 5 minutes | `ipmg --input targets.txt --interval 5` |
407
- | Look back at earlier scans | `ipmg history` |
408
- | Compare two specific scans | `ipmg diff 12 14` |
409
- | Use IPMG Web in your browser instead | `ipmg web` |
410
- | See every available flag | `ipmg --help` |
411
-
412
- ---
413
-
414
- ## Live results
415
-
416
- By default a scan prints its results once every host has been probed. On a
417
- large range that is a long wait with nothing to look at, so `--stream` prints
418
- each host the moment its probe finishes, above a progress bar that also carries
419
- a running count of the hosts that answered:
420
-
421
- ```bash
422
- ipmg --input 192.168.1.0/24 --stream
423
- ```
424
-
425
- ```text
426
- Live
427
- Status Host Latency
428
- ● Active 192.168.1.1 0.9 ms
429
- ● Active 192.168.1.24 3.1 ms
430
- ⠹ Scanning ━━━━━━━━━━━─────────── 48% 122/254 0:00:09 2 up
431
- ```
432
-
433
- `--stream` shows only the hosts that answer, which is what makes a sparse range
434
- readable. Add `--stream-all` to see every result, including timeouts and
435
- unreachable hosts. The rows gain a `Name` column under `--resolve` and an
436
- `Open ports` column under `--scan-ports`.
437
-
438
- Streaming costs nothing in scan time: rows are printed by the thread that
439
- collects results, so the workers never wait on the terminal. When output is
440
- piped or redirected the progress bar is dropped and the rows are written as
441
- plain lines, which makes `ipmg --stream-all >> scan.log` a usable live log.
442
-
443
- ---
444
-
445
414
  ## Change detection
446
415
 
447
416
  Every scan is stored in a local SQLite history (`~/.ipmg/dashboard.db`), shared
@@ -458,6 +427,13 @@ ipmg diff --fail-on-change # exit 2 when anything changed (CI)
458
427
  ipmg history --limit 10 # list stored scans
459
428
  ```
460
429
 
430
+ The same comparison is the **Changes** view in [IPMG Web](#ipmg-web), with
431
+ the summary exportable as Markdown, JSON, or CSV:
432
+
433
+ <p align="center">
434
+ <img src="https://raw.githubusercontent.com/sameeralam3127/ipmg/main/docs/assets/ipmg-changes.png" alt="IPMG Web Changes view comparing scan 23 with scan 24: 4 changes, 1 critical. A host back online, a latency change of +6.2 ms, a host offline, and a status change from Timeout to Inactive" width="820">
435
+ </p>
436
+
461
437
  To hear about changes without watching the terminal, send them to Slack,
462
438
  Microsoft Teams, any JSON webhook, or email. Any `--notify-*` flag turns on
463
439
  `--compare`, and with `--interval` every pass that changes something alerts:
@@ -481,7 +457,7 @@ What counts as a change:
481
457
  | New host | warning | An IP that the baseline never saw |
482
458
  | Host removed | warning | An IP the current scan no longer covers |
483
459
  | IP address changed | warning | A known hostname moved to a different IP |
484
- | Service changed | warning | Status moved between failure modes (e.g. `Timeout` → `Unreachable`) |
460
+ | Status changed | warning | Status moved between failure modes (e.g. `Timeout` → `Unreachable`) |
485
461
  | Host back online | info | Recovered since the baseline |
486
462
  | Hostname changed | info | Same IP, different PTR record |
487
463
  | Latency changed | info | Latency moved past both thresholds |
@@ -513,6 +489,75 @@ source**, so file-based and `--discover` runs do not get mixed up. Pass
513
489
 
514
490
  ---
515
491
 
492
+ ## Common tasks
493
+
494
+ Not sure which flags you need? The
495
+ [command builder](https://sameeralam3127.github.io/ipmg/#builder) on the
496
+ website puts the command together as you pick what you want to know, and
497
+ explains every flag it adds.
498
+
499
+ <p align="center">
500
+ <a href="https://sameeralam3127.github.io/ipmg/#builder">
501
+ <img src="https://raw.githubusercontent.com/sameeralam3127/ipmg/main/docs/assets/ipmg-builder.png" alt="IPMG command builder: choose Scan, Compare, History, or Web, enter a target such as 192.168.1.0/24, switch on options like live results or hostnames, and copy the generated ipmg command with each flag explained" width="820">
502
+ </a>
503
+ </p>
504
+
505
+ Or pick a ready-made command:
506
+
507
+ | I want to… | Command |
508
+ | --- | --- |
509
+ | Scan the network I am on | `ipmg --discover` |
510
+ | Scan hosts listed in a file | `ipmg --input targets.txt` |
511
+ | Scan a file plus a few extra hosts | `ipmg --input targets.txt 10.0.0.0/30 10.0.0.5` |
512
+ | Get names, not just IP addresses | `ipmg --input targets.txt --resolve` |
513
+ | Get a report I can send to someone | `ipmg --input targets.txt --formats md csv` |
514
+ | Pipe the results into a script | `ipmg --input targets.txt --json \| jq .` |
515
+ | See hosts appear as they answer | `ipmg --input 192.168.1.0/24 --stream` |
516
+ | See what changed since last time | `ipmg --input targets.txt --compare` |
517
+ | Check which services are listening | `ipmg --input targets.txt --scan-ports` |
518
+ | Keep scanning every 5 minutes | `ipmg --input targets.txt --interval 5` |
519
+ | Look back at earlier scans | `ipmg history` |
520
+ | Compare two specific scans | `ipmg diff 12 14` |
521
+ | Use IPMG Web in your browser instead | `ipmg web` |
522
+ | See every available flag | `ipmg --help` |
523
+
524
+ ---
525
+
526
+ ## Live results
527
+
528
+ By default a scan prints its results once every host has been probed. On a
529
+ large range that is a long wait with nothing to look at, so `--stream` prints
530
+ each host the moment its probe finishes, above a progress bar that also carries
531
+ a running count of the hosts that answered:
532
+
533
+ ```bash
534
+ ipmg --input 192.168.1.0/24 --stream
535
+ ```
536
+
537
+ <p align="center">
538
+ <img src="https://raw.githubusercontent.com/sameeralam3127/ipmg/main/docs/assets/ipmg-demo.gif" alt="ipmg scanning 13 hosts in parallel: live results with reverse DNS names and latency, then a summary of 10 active and 3 timed out" width="820">
539
+ </p>
540
+
541
+ ```text
542
+ Live
543
+ Status Host Latency
544
+ ● Active 192.168.1.1 0.9 ms
545
+ ● Active 192.168.1.24 3.1 ms
546
+ ⠹ Scanning ━━━━━━━━━━━─────────── 48% 122/254 0:00:09 2 up
547
+ ```
548
+
549
+ `--stream` shows only the hosts that answer, which is what makes a sparse range
550
+ readable. Add `--stream-all` to see every result, including timeouts and
551
+ unreachable hosts. The rows gain a `Name` column under `--resolve` and an
552
+ `Open ports` column under `--scan-ports`.
553
+
554
+ Streaming costs nothing in scan time: rows are printed by the thread that
555
+ collects results, so the workers never wait on the terminal. When output is
556
+ piped or redirected the progress bar is dropped and the rows are written as
557
+ plain lines, which makes `ipmg --stream-all >> scan.log` a usable live log.
558
+
559
+ ---
560
+
516
561
  ## IPMG Web
517
562
 
518
563
  Prefer clicking to typing? IPMG Web runs locally and shares the CLI's
@@ -5,22 +5,21 @@
5
5
  ![License](https://img.shields.io/badge/license-MIT-green)
6
6
  [![Publish](https://github.com/sameeralam3127/ipmg/actions/workflows/publish.yml/badge.svg)](https://github.com/sameeralam3127/ipmg/actions/workflows/publish.yml)
7
7
 
8
- **Find out which hosts on your network are up — and what changed since last time.**
8
+ **Know what changed on your network since the last scan.**
9
9
 
10
- IPMG pings hosts in parallel, resolves their names, and hands you a report you
11
- can send to someone: Excel, CSV, JSON, or Markdown. It works from the command
12
- line or from IPMG Web, a local browser UI, and it remembers every scan so it can tell
13
- you what moved.
10
+ IPMG keeps a history of every scan and tells you what moved between two of
11
+ them: hosts that went offline or appeared, IP addresses and hostnames that
12
+ moved, latency that shifted. Every change carries a severity, so you can send
13
+ only the ones that matter to Slack, Teams, a webhook, or email, or fail a cron
14
+ job or CI step with an exit code. Around that: parallel ping scanning of IPs,
15
+ CIDR blocks, and ranges (IPv4 and IPv6), IPMG Web, a local dashboard that
16
+ works offline, and reports in Excel, CSV, JSON, and Markdown.
14
17
 
15
18
  <p align="center">
16
- <img src="https://raw.githubusercontent.com/sameeralam3127/ipmg/main/docs/assets/ipmg-demo.gif" alt="ipmg scanning 13 hosts in parallel: live results with reverse DNS names and latency, then a summary of 10 active and 3 timed out" width="820">
19
+ <img src="https://raw.githubusercontent.com/sameeralam3127/ipmg/main/docs/assets/ipmg-compare.gif" alt="ipmg scans 13 hosts, a new address is added to the target file, and a second scan with --compare reports a new host (warning) and a latency change (info) against the first" width="820">
17
20
  </p>
18
21
 
19
- **Why not `nmap -sn`, `fping`, or Angry IP Scanner?** They tell you what is up
20
- right now. IPMG also remembers every scan and tells you what changed since the
21
- last one: the host that dropped off, the device that appeared, the latency that
22
- doubled. It writes the report you would otherwise build by hand.
23
- [How it compares](#how-it-compares)
22
+ [How it compares](#how-it-compares) to `nmap -sn`, `fping`, and Angry IP Scanner.
24
23
 
25
24
  **Website:** [sameeralam3127.github.io/ipmg](https://sameeralam3127.github.io/ipmg/) ·
26
25
  **Live demo:** [IPMG Web with sample data](https://sameeralam3127.github.io/ipmg/demo/) ·
@@ -28,7 +27,8 @@ doubled. It writes the report you would otherwise build by hand.
28
27
 
29
28
  ```bash
30
29
  pip install ipmg
31
- ipmg --discover # scan the network you are on, right now
30
+ ipmg --discover # scan the network you are on, right now
31
+ ipmg --discover --compare # later: scan again and see what changed
32
32
  ```
33
33
 
34
34
  > **Please read:** only scan networks you are authorized to scan. Unauthorized
@@ -37,11 +37,12 @@ ipmg --discover # scan the network you are on, right now
37
37
  **Contents**
38
38
 
39
39
  [How it compares](#how-it-compares) ·
40
+ [Requirements and platform support](#requirements-and-platform-support) ·
40
41
  [Install](#install) ·
41
42
  [Your first scan](#your-first-scan) ·
43
+ [Change detection](#change-detection) ·
42
44
  [Common tasks](#common-tasks) ·
43
45
  [Live results](#live-results) ·
44
- [Change detection](#change-detection) ·
45
46
  [IPMG Web](#ipmg-web) ·
46
47
  [What you can scan](#what-you-can-scan) ·
47
48
  [Reports](#reports) ·
@@ -56,15 +57,22 @@ ipmg --discover # scan the network you are on, right now
56
57
 
57
58
  | | IPMG | `nmap -sn` | `fping` | Angry IP Scanner |
58
59
  | --- | --- | --- | --- | --- |
59
- | Parallel ping sweep | Yes | Yes | Yes | Yes |
60
+ | Parallel ping sweep | Yes, one system `ping` per host | Yes | Yes, fastest of the four | Yes |
60
61
  | Scan history and "what changed" | Built in | Save XML, compare with `ndiff` | No | No |
62
+ | Change severities and alerts | critical / warning / info; Slack, Teams, webhook, email | No | No | No |
63
+ | Exit codes for cron and CI | `3` with `--fail-on-down`, `2` with `diff --fail-on-change` | No | Non-zero if any host is unreachable | No (GUI first) |
61
64
  | Reports | Excel, CSV, JSON, Markdown | XML, grepable text | Plain text | CSV, TXT, XML |
62
- | Browser UI | IPMG Web, local | No (Zenmap is a desktop app) | No | Desktop app (Java) |
63
- | Port checks | Common TCP ports | Full port and OS scanner | No | Via fetchers |
65
+ | Browser UI | IPMG Web, local, works offline | No (Zenmap is a desktop app) | No | Desktop app (Java) |
66
+ | Port checks | Common TCP ports (connect only) | Full port, service, and OS scanner | No | Via fetchers |
64
67
 
65
- Reach for nmap when you need a real port or OS scanner. Reach for IPMG when you
66
- look after a network and need to know what moved since yesterday, with a report
67
- you can hand to someone.
68
+ Where the others are better: **nmap** is a real port, service, and OS
69
+ scanner, and IPMG does not try to be one. **fping** sends every probe from a
70
+ single process, so on large ranges it is much faster than IPMG, which starts
71
+ one `ping` per host (`--threads` at a time). Angry IP Scanner is a
72
+ point-and-click desktop app with a plugin system.
73
+
74
+ Reach for IPMG when you look after a network and need to know what moved since
75
+ yesterday, with a report you can hand to someone.
68
76
 
69
77
  <p align="center">
70
78
  <img src="https://raw.githubusercontent.com/sameeralam3127/ipmg/main/docs/assets/ipmg-web.png" alt="IPMG Web dashboard: scan totals, a status donut of 16 active, 1 timeout and 1 inactive host, a latency trend chart, and a list of recent scans" width="820">
@@ -72,6 +80,32 @@ you can hand to someone.
72
80
 
73
81
  ---
74
82
 
83
+ ## Requirements and platform support
84
+
85
+ IPMG does not craft packets. It runs your operating system's `ping` command
86
+ once per host, as a direct process call with no shell, and reads its output.
87
+ That means:
88
+
89
+ - **Python 3.9 or newer**, unless you use the one-line installers, which
90
+ bring their own.
91
+ - **The system `ping` command.** macOS and Windows include it; minimal Linux
92
+ and container images may not ([how to install it](#the-one-thing-ipmg-needs-from-your-system)).
93
+ - **No root or administrator rights.** IPMG has exactly the privileges your
94
+ `ping` has: if `ping 8.8.8.8` works for your user, a scan works too. Port
95
+ checks (`--scan-ports`) are ordinary TCP connections.
96
+
97
+ | Platform | What IPMG runs | Status |
98
+ | --- | --- | --- |
99
+ | Linux (iputils or busybox `ping`) | `ping -c COUNT -W SECONDS` | Supported. CI runs the tests and a scan on Ubuntu; see [verified environments](#verified-environments) |
100
+ | macOS | `ping -c COUNT -W MILLISECONDS`; `ping6` for IPv6 | Supported. CI runs the tests and a scan on macOS |
101
+ | Windows | `ping -n COUNT -w MILLISECONDS` | Supported. CI runs the tests and a scan of `127.0.0.1` on `windows-latest`; not yet verified against a real LAN |
102
+ | FreeBSD, OpenBSD, NetBSD | the macOS flags | Untested. The code treats them like macOS, and their `ping` options may differ |
103
+
104
+ `--discover ipv6` reads the neighbour table with `ip -6 neigh` on Linux (the
105
+ `iproute2` package), `ndp -an` on macOS, and `netsh` on Windows.
106
+
107
+ ---
108
+
75
109
  ## Install
76
110
 
77
111
  **Linux and macOS — one command, works on every distribution:**
@@ -325,71 +359,6 @@ exist yet. A file you name with `--input` must already exist.
325
359
 
326
360
  ---
327
361
 
328
- ## Common tasks
329
-
330
- Not sure which flags you need? The
331
- [command builder](https://sameeralam3127.github.io/ipmg/#builder) on the
332
- website puts the command together as you pick what you want to know, and
333
- explains every flag it adds.
334
-
335
- <p align="center">
336
- <a href="https://sameeralam3127.github.io/ipmg/#builder">
337
- <img src="https://raw.githubusercontent.com/sameeralam3127/ipmg/main/docs/assets/ipmg-builder.png" alt="IPMG command builder: choose Scan, Compare, History, or Web, enter a target such as 192.168.1.0/24, switch on options like live results or hostnames, and copy the generated ipmg command with each flag explained" width="820">
338
- </a>
339
- </p>
340
-
341
- Or pick a ready-made command:
342
-
343
- | I want to… | Command |
344
- | --- | --- |
345
- | Scan the network I am on | `ipmg --discover` |
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` |
348
- | Get names, not just IP addresses | `ipmg --input targets.txt --resolve` |
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 .` |
351
- | See hosts appear as they answer | `ipmg --input 192.168.1.0/24 --stream` |
352
- | See what changed since last time | `ipmg --input targets.txt --compare` |
353
- | Check which services are listening | `ipmg --input targets.txt --scan-ports` |
354
- | Keep scanning every 5 minutes | `ipmg --input targets.txt --interval 5` |
355
- | Look back at earlier scans | `ipmg history` |
356
- | Compare two specific scans | `ipmg diff 12 14` |
357
- | Use IPMG Web in your browser instead | `ipmg web` |
358
- | See every available flag | `ipmg --help` |
359
-
360
- ---
361
-
362
- ## Live results
363
-
364
- By default a scan prints its results once every host has been probed. On a
365
- large range that is a long wait with nothing to look at, so `--stream` prints
366
- each host the moment its probe finishes, above a progress bar that also carries
367
- a running count of the hosts that answered:
368
-
369
- ```bash
370
- ipmg --input 192.168.1.0/24 --stream
371
- ```
372
-
373
- ```text
374
- Live
375
- Status Host Latency
376
- ● Active 192.168.1.1 0.9 ms
377
- ● Active 192.168.1.24 3.1 ms
378
- ⠹ Scanning ━━━━━━━━━━━─────────── 48% 122/254 0:00:09 2 up
379
- ```
380
-
381
- `--stream` shows only the hosts that answer, which is what makes a sparse range
382
- readable. Add `--stream-all` to see every result, including timeouts and
383
- unreachable hosts. The rows gain a `Name` column under `--resolve` and an
384
- `Open ports` column under `--scan-ports`.
385
-
386
- Streaming costs nothing in scan time: rows are printed by the thread that
387
- collects results, so the workers never wait on the terminal. When output is
388
- piped or redirected the progress bar is dropped and the rows are written as
389
- plain lines, which makes `ipmg --stream-all >> scan.log` a usable live log.
390
-
391
- ---
392
-
393
362
  ## Change detection
394
363
 
395
364
  Every scan is stored in a local SQLite history (`~/.ipmg/dashboard.db`), shared
@@ -406,6 +375,13 @@ ipmg diff --fail-on-change # exit 2 when anything changed (CI)
406
375
  ipmg history --limit 10 # list stored scans
407
376
  ```
408
377
 
378
+ The same comparison is the **Changes** view in [IPMG Web](#ipmg-web), with
379
+ the summary exportable as Markdown, JSON, or CSV:
380
+
381
+ <p align="center">
382
+ <img src="https://raw.githubusercontent.com/sameeralam3127/ipmg/main/docs/assets/ipmg-changes.png" alt="IPMG Web Changes view comparing scan 23 with scan 24: 4 changes, 1 critical. A host back online, a latency change of +6.2 ms, a host offline, and a status change from Timeout to Inactive" width="820">
383
+ </p>
384
+
409
385
  To hear about changes without watching the terminal, send them to Slack,
410
386
  Microsoft Teams, any JSON webhook, or email. Any `--notify-*` flag turns on
411
387
  `--compare`, and with `--interval` every pass that changes something alerts:
@@ -429,7 +405,7 @@ What counts as a change:
429
405
  | New host | warning | An IP that the baseline never saw |
430
406
  | Host removed | warning | An IP the current scan no longer covers |
431
407
  | IP address changed | warning | A known hostname moved to a different IP |
432
- | Service changed | warning | Status moved between failure modes (e.g. `Timeout` → `Unreachable`) |
408
+ | Status changed | warning | Status moved between failure modes (e.g. `Timeout` → `Unreachable`) |
433
409
  | Host back online | info | Recovered since the baseline |
434
410
  | Hostname changed | info | Same IP, different PTR record |
435
411
  | Latency changed | info | Latency moved past both thresholds |
@@ -461,6 +437,75 @@ source**, so file-based and `--discover` runs do not get mixed up. Pass
461
437
 
462
438
  ---
463
439
 
440
+ ## Common tasks
441
+
442
+ Not sure which flags you need? The
443
+ [command builder](https://sameeralam3127.github.io/ipmg/#builder) on the
444
+ website puts the command together as you pick what you want to know, and
445
+ explains every flag it adds.
446
+
447
+ <p align="center">
448
+ <a href="https://sameeralam3127.github.io/ipmg/#builder">
449
+ <img src="https://raw.githubusercontent.com/sameeralam3127/ipmg/main/docs/assets/ipmg-builder.png" alt="IPMG command builder: choose Scan, Compare, History, or Web, enter a target such as 192.168.1.0/24, switch on options like live results or hostnames, and copy the generated ipmg command with each flag explained" width="820">
450
+ </a>
451
+ </p>
452
+
453
+ Or pick a ready-made command:
454
+
455
+ | I want to… | Command |
456
+ | --- | --- |
457
+ | Scan the network I am on | `ipmg --discover` |
458
+ | Scan hosts listed in a file | `ipmg --input targets.txt` |
459
+ | Scan a file plus a few extra hosts | `ipmg --input targets.txt 10.0.0.0/30 10.0.0.5` |
460
+ | Get names, not just IP addresses | `ipmg --input targets.txt --resolve` |
461
+ | Get a report I can send to someone | `ipmg --input targets.txt --formats md csv` |
462
+ | Pipe the results into a script | `ipmg --input targets.txt --json \| jq .` |
463
+ | See hosts appear as they answer | `ipmg --input 192.168.1.0/24 --stream` |
464
+ | See what changed since last time | `ipmg --input targets.txt --compare` |
465
+ | Check which services are listening | `ipmg --input targets.txt --scan-ports` |
466
+ | Keep scanning every 5 minutes | `ipmg --input targets.txt --interval 5` |
467
+ | Look back at earlier scans | `ipmg history` |
468
+ | Compare two specific scans | `ipmg diff 12 14` |
469
+ | Use IPMG Web in your browser instead | `ipmg web` |
470
+ | See every available flag | `ipmg --help` |
471
+
472
+ ---
473
+
474
+ ## Live results
475
+
476
+ By default a scan prints its results once every host has been probed. On a
477
+ large range that is a long wait with nothing to look at, so `--stream` prints
478
+ each host the moment its probe finishes, above a progress bar that also carries
479
+ a running count of the hosts that answered:
480
+
481
+ ```bash
482
+ ipmg --input 192.168.1.0/24 --stream
483
+ ```
484
+
485
+ <p align="center">
486
+ <img src="https://raw.githubusercontent.com/sameeralam3127/ipmg/main/docs/assets/ipmg-demo.gif" alt="ipmg scanning 13 hosts in parallel: live results with reverse DNS names and latency, then a summary of 10 active and 3 timed out" width="820">
487
+ </p>
488
+
489
+ ```text
490
+ Live
491
+ Status Host Latency
492
+ ● Active 192.168.1.1 0.9 ms
493
+ ● Active 192.168.1.24 3.1 ms
494
+ ⠹ Scanning ━━━━━━━━━━━─────────── 48% 122/254 0:00:09 2 up
495
+ ```
496
+
497
+ `--stream` shows only the hosts that answer, which is what makes a sparse range
498
+ readable. Add `--stream-all` to see every result, including timeouts and
499
+ unreachable hosts. The rows gain a `Name` column under `--resolve` and an
500
+ `Open ports` column under `--scan-ports`.
501
+
502
+ Streaming costs nothing in scan time: rows are printed by the thread that
503
+ collects results, so the workers never wait on the terminal. When output is
504
+ piped or redirected the progress bar is dropped and the rows are written as
505
+ plain lines, which makes `ipmg --stream-all >> scan.log` a usable live log.
506
+
507
+ ---
508
+
464
509
  ## IPMG Web
465
510
 
466
511
  Prefer clicking to typing? IPMG Web runs locally and shares the CLI's
@@ -13,7 +13,7 @@ build-backend = "setuptools.build_meta"
13
13
 
14
14
  [project]
15
15
  name = "ipmg"
16
- version = "3.1.0" # Managed automatically by semantic-release
16
+ version = "3.1.2" # 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"
@@ -2,4 +2,4 @@
2
2
  ipmg - IP Management & Ping Monitoring Tool
3
3
  """
4
4
 
5
- __version__ = "3.1.0"
5
+ __version__ = "3.1.2"
@@ -64,7 +64,9 @@ CHANGE_LABELS: Dict[ChangeType, str] = {
64
64
  ChangeType.IP_CHANGED: "IP address changed",
65
65
  ChangeType.HOSTNAME_CHANGED: "Hostname changed",
66
66
  ChangeType.LATENCY_CHANGED: "Latency changed",
67
- ChangeType.SERVICE_CHANGED: "Service changed",
67
+ # The key stays "service_changed" for existing JSON consumers; the change
68
+ # is a move between failure modes (Timeout -> Unreachable), not a service.
69
+ ChangeType.SERVICE_CHANGED: "Status changed",
68
70
  }
69
71
 
70
72
 
@@ -146,7 +146,13 @@ def ping_ip(ip: str, timeout: int, count: int) -> Tuple[str, Optional[float]]:
146
146
 
147
147
  latency = parse_latency(result.stdout)
148
148
 
149
- if result.returncode == 0:
149
+ answered = result.returncode == 0
150
+ if answered and latency is None and platform.system().lower() == "windows":
151
+ # Windows ping also exits 0 when a router replies "Destination host
152
+ # unreachable"; only a real echo reply prints round-trip times.
153
+ answered = False
154
+
155
+ if answered:
150
156
  return "Active", latency
151
157
 
152
158
  output = result.stdout.lower()