Multi-host ping monitor with a rich static HTML report.
Project description
emping
Ping many hosts at once and produce a rich, self-contained HTML report, all built to deliver a nice report and a clean, reusable Python API.
emping is both:
- a library you can drop into other projects (
from emping import Monitor), and - a CLI app (
emping 8.8.8.8 1.1.1.1 -c 10).
Highlights
- Auto-selecting ping backend. Uses unprivileged ICMP datagram sockets
when the OS allows (macOS, modern Linux) for precise RTT/TTL, and falls back to
the system
pingcommand otherwise (Windows, locked-down Linux). No root required in the common case. IPv4 and IPv6 are supported. - Continuous or fixed-count monitoring, on a steady cadence. Built for network migrations where minutes or hours of downtime needs to be monitored and recorded.
- A report that's clear: summary cards (up/degraded/down, overall loss, average
& slowest host), click-to-sort columns (numeric-aware), a live text filter,
color-coded health, per-host latency sparklines with packet-loss ticks, and
light/dark themes. All grafted into a single
.htmlfile that works offline and is easy to distribute.
Install
pip install emping
Or from a checkout of this repository:
pip install -e . # editable install, from this directory
CLI usage
# Fixed count: 5 pings per host, then write the report and exit
emping 8.8.8.8 1.1.1.1 9.9.9.9 -c 5 -o report.html
# Continuous monitoring until Ctrl-C, updating the report as it goes
emping -f hosts.json --interval 2 -o report.html --open
# Read targets (with descriptions) from a file, auto-refresh the report in-browser
emping -f hosts.json --refresh 5 -o report.html
Without installing, use python -m emping ….
Key options
| Option | Meaning | Default |
|---|---|---|
hosts … |
targets to ping (positional) | — |
-f, --file |
read targets from a JSON file | — |
-i, --interval |
seconds between rounds | 1.0 |
-c, --count |
pings per host; 0 = continuous until Ctrl-C |
0 |
-t, --timeout |
per-ping timeout (seconds) | 1.0 |
-o, --output |
HTML report path | ping_report.html |
--backend |
auto | socket | subprocess |
auto |
--refresh |
embed an auto-refresh (seconds) in the report | 0 (off) |
--report-every |
min seconds between report writes while running | 2.0 |
--workers |
max concurrent pings | min(hosts, 64) |
--open |
open the report in a browser after first write | off |
-q, --quiet |
suppress per-round console output | off |
Targets file format
-f/--file reads targets from a JSON file:
{
"hosts": [
{ "host": "8.8.8.8", "description": "Google DNS" },
{ "host": "192.0.2.1", "description": "Lab gateway", "site": "sto1" },
"one.one.one.one"
]
}
Schema:
| top level | an object with a hosts array, or the array on its own |
| entry | a string (the host), or an object |
host |
required, string — address or DNS name |
description |
optional, string — shown in the report (default "") |
| other keys | ignored, so the file can double as an inventory carrying site, owner, ticket refs, etc. |
Anything malformed — bad JSON, a missing host, a non-string value — exits 2
with a message naming the offending entry, e.g. hosts[3]: object entry is missing the required 'host' key. A full example is in hosts.example.json.
Run mode is chosen with -c: give a count for a batch run, or omit it (or pass
0) for continuous monitoring. Continuous runs update the report periodically
and always write a final report on Ctrl-C.
Library usage
from emping import Monitor, Host, write_report, ReportMeta
monitor = Monitor(
[Host("8.8.8.8", "Google DNS"), Host("1.1.1.1", "Cloudflare")],
interval=1.0,
count=10, # 0 for continuous; call monitor.stop() from another thread
timeout=1.0,
)
snapshots = monitor.run() # blocks; returns list[HostSnapshot]
write_report("report.html", snapshots, ReportMeta(title="My Report"))
For live/streaming use, pass callbacks:
def on_round(round_num, snapshots):
write_report("report.html", snapshots) # atomic write, safe to re-read live
Monitor(hosts, count=0, on_round=on_round,
on_result=lambda i, r: print(r.host, r.rtt_ms)).run()
Targets files are loadable directly, without going through the CLI:
from emping import load_hosts, HostFileError
try:
hosts = load_hosts("hosts.json") # -> list[Host]; fmt="json"|"text" to force
except HostFileError as exc:
... # malformed file; OSError if unreadable
Lower-level pieces are exported too: get_pinger(), PingResult, HostStats,
render_report(), build_sparkline(), parse_hosts_json().
How pinging works (and privileges)
get_pinger(backend="auto") probes for an unprivileged ICMP datagram socket:
socketbackend —SOCK_DGRAM+IPPROTO_ICMP/IPPROTO_ICMPV6. Precise RTT and TTL. Works without root on macOS/BSD and on Linux wherenet.ipv4.ping_group_rangepermits it. Reply parsing is platform-aware (macOS includes the IP header, Linux strips it; TTL comes from the header orrecvmsgancillary data).subprocessbackend — shells out to the systemping. The portable fallback (Windows, locked-down Linux). TTL/reply-IP are parsed from the output.
Force one with --backend socket / --backend subprocess.
Known limitation
On macOS, the kernel does not deliver loopback (127.0.0.1 / ::1)
ICMP replies to unprivileged datagram sockets — even the system ping shows no
reply line. Loopback isn't a realistic monitoring target, but if you must ping it
on macOS, use --backend subprocess (which still returns exit status) or expect
it to read as "down". Remote hosts are unaffected.
Testing
The package uses a src/ layout, so install it before running the tests —
pytest from the repository root will not otherwise find it.
pip install -e ".[dev]"
pytest -q # run unit tests (stats, report, parsing, checksum)
Building a release
pip install build twine
python -m build # writes dist/*.whl and dist/*.tar.gz
twine check dist/*
Project layout
pyproject.toml
LICENSE
README.md
src/emping/
pinger.py # ICMP datagram + subprocess backends, auto-selection
stats.py # HostStats accumulator + immutable HostSnapshot
hosts.py # JSON targets file loading + validation
monitor.py # fixed-cadence, concurrent multi-host ping loop
report.py # self-contained HTML rendering (Jinja2), atomic writes
cli.py # argparse CLI
templates/report.html.j2
tests/test_emping.py
License
MIT.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file emping-0.1.0.tar.gz.
File metadata
- Download URL: emping-0.1.0.tar.gz
- Upload date:
- Size: 28.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2596d644dd058e22fa9de6b4b7dbbbf117aac3f4056022d4d6d7f5c0561eab74
|
|
| MD5 |
48af1e0ca913fc4af44d2ddd43c5eadb
|
|
| BLAKE2b-256 |
ea5c7df2c5381dbed020a82bcc77a6cfddeec4d0cbb51f6de047c396fb6df03b
|
File details
Details for the file emping-0.1.0-py3-none-any.whl.
File metadata
- Download URL: emping-0.1.0-py3-none-any.whl
- Upload date:
- Size: 25.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0ae70b2896e397248bfd8afe5588b3ef4d179326dde922972e5d5c81e93fb7e9
|
|
| MD5 |
fad5e598623864056767d3cac483b6aa
|
|
| BLAKE2b-256 |
39bf1f431837c50b39e328c8125a33ec22d79b7548717893e4af21ad09736553
|