isitup-cli 0.1.3__tar.gz → 0.2.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.
- isitup_cli-0.2.0/PKG-INFO +310 -0
- isitup_cli-0.2.0/README.md +289 -0
- {isitup_cli-0.1.3 → isitup_cli-0.2.0}/pyproject.toml +2 -1
- {isitup_cli-0.1.3 → isitup_cli-0.2.0}/pyproject.toml.orig +2 -1
- isitup_cli-0.2.0/src/isitup/__init__.py +3 -0
- {isitup_cli-0.1.3 → isitup_cli-0.2.0}/src/isitup/checks.py +98 -15
- isitup_cli-0.2.0/src/isitup/cli.py +333 -0
- isitup_cli-0.2.0/src/isitup/config.py +364 -0
- isitup_cli-0.2.0/src/isitup/history.py +112 -0
- {isitup_cli-0.1.3 → isitup_cli-0.2.0}/src/isitup/output.py +50 -8
- {isitup_cli-0.1.3 → isitup_cli-0.2.0}/src/isitup/render.py +116 -44
- isitup_cli-0.1.3/PKG-INFO +0 -172
- isitup_cli-0.1.3/README.md +0 -152
- isitup_cli-0.1.3/src/isitup/__init__.py +0 -3
- isitup_cli-0.1.3/src/isitup/cli.py +0 -190
- isitup_cli-0.1.3/src/isitup/config.py +0 -160
- isitup_cli-0.1.3/src/isitup/history.py +0 -62
- {isitup_cli-0.1.3 → isitup_cli-0.2.0}/LICENSE +0 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: isitup-cli
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A small CLI that periodically checks whether servers respond to ping, HTTP, and TCP ports
|
|
5
|
+
Author: stefan.insam
|
|
6
|
+
Author-email: stefan.insam <stefan.insam@netgo.de>
|
|
7
|
+
License-Expression: GPL-3.0-only
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: System Administrators
|
|
12
|
+
Classifier: Topic :: System :: Networking :: Monitoring
|
|
13
|
+
Classifier: Topic :: Utilities
|
|
14
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
15
|
+
Requires-Dist: requests>=2.34.2
|
|
16
|
+
Requires-Dist: rich>=15.0.0
|
|
17
|
+
Requires-Python: >=3.14
|
|
18
|
+
Project-URL: Repository, https://github.com/ramsesoriginal/isitup
|
|
19
|
+
Project-URL: Issues, https://github.com/ramsesoriginal/isitup/issues
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
<div align="center">
|
|
23
|
+
<img src="https://raw.githubusercontent.com/ramsesoriginal/isitup/main/logo/main_hero.svg" alt="isitup" width="720">
|
|
24
|
+
|
|
25
|
+
<p><strong>Is it up? Ping, HTTP, and TCP checks in one small, fast CLI.</strong></p>
|
|
26
|
+
|
|
27
|
+
[](https://pypi.org/project/isitup-cli/)
|
|
28
|
+
[](https://github.com/ramsesoriginal/isitup/actions/workflows/ci.yml)
|
|
29
|
+
[](LICENSE)
|
|
30
|
+
[](pyproject.toml)
|
|
31
|
+
[](https://github.com/astral-sh/uv)
|
|
32
|
+
[](https://github.com/astral-sh/ruff)
|
|
33
|
+
[](https://mypy-lang.org/)
|
|
34
|
+
[](https://github.com/ramsesoriginal/isitup)
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
**isitup** is a small CLI that periodically checks whether servers respond to ping and,
|
|
40
|
+
depending on how the target is written, HTTP, a TCP port, or SSH:
|
|
41
|
+
|
|
42
|
+
| Target | Checks |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| `example.com` | ping only |
|
|
45
|
+
| `example.com:1337` | ping + TCP connect to port 1337 |
|
|
46
|
+
| `http://example.com` / `https://example.com` | ping + HTTP (catching a 500, a DNS failure, an expired TLS certificate, a redirect loop, etc.) |
|
|
47
|
+
| `https://example.com:1337` | ping + HTTP, against that port |
|
|
48
|
+
| `ssh://example.com` / `ssh://example.com:1337` | ping + SSH (connects and verifies the server's SSH identification banner; defaults to port 22) |
|
|
49
|
+
|
|
50
|
+
Ping is checked independently from the HTTP/TCP/SSH probe: many hosts (behind
|
|
51
|
+
load balancers, CDNs, or firewalls) drop ICMP but serve their actual service
|
|
52
|
+
just fine, so a blocked ping alone doesn't mark a target down: the "Status"
|
|
53
|
+
column is based on the probe, with ping shown alongside as extra diagnostic
|
|
54
|
+
info. A target only shows as `OFFLINE` when both ping and the probe fail; a
|
|
55
|
+
ping-only target's status is based on ping alone, since there's nothing else
|
|
56
|
+
to check.
|
|
57
|
+
|
|
58
|
+
If the same host appears more than once (e.g. `example.com` and
|
|
59
|
+
`https://example.com/health`), it's only pinged once per round. The ping
|
|
60
|
+
result is shared across all targets pointing at that host.
|
|
61
|
+
|
|
62
|
+
While checks are running you get a live spinner and a progress bar per
|
|
63
|
+
target counting down its timeout; results are then shown in a table that
|
|
64
|
+
updates in place (via [`rich`](https://github.com/Textualize/rich)) rather
|
|
65
|
+
than reprinting on every round.
|
|
66
|
+
|
|
67
|
+
Each target also keeps a rolling window of its last 20 response times for
|
|
68
|
+
the session (HTTP response time, TCP connect time, or SSH connect+banner
|
|
69
|
+
time, whichever applies), shown as Min/Max latency columns and a tiny
|
|
70
|
+
sparkline ("Trend"), along with
|
|
71
|
+
the last time it was seen online and the last time it was seen offline.
|
|
72
|
+
This history is in-memory only and resets each time you start the tool.
|
|
73
|
+
|
|
74
|
+
## Installation
|
|
75
|
+
|
|
76
|
+
The package is published on PyPI as `isitup-cli`, but it installs a plain
|
|
77
|
+
`isitup` command:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
uv tool install isitup-cli
|
|
81
|
+
# or: pipx install isitup-cli
|
|
82
|
+
# or: pip install isitup-cli
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
To try unreleased changes straight from `main` instead (no PyPI release
|
|
86
|
+
needed):
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
uv tool install git+https://github.com/ramsesoriginal/isitup
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Either way, remove it later with `uv tool uninstall isitup`.
|
|
93
|
+
|
|
94
|
+
## Usage
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
isitup example.com https://example.org db.internal:5432
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`--url`/`-u` work the same way and can be freely mixed with positional
|
|
101
|
+
targets: `isitup example.com -u https://example.org` is the same as
|
|
102
|
+
`isitup example.com https://example.org`:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
isitup --url example.com --url https://example.org --url db.internal:5432
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Or with a config file:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
cp config.example.yaml config.yaml
|
|
112
|
+
# edit config.yaml
|
|
113
|
+
isitup --config config.yaml
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Config files and CLI-supplied targets (positional or `--url`) can be
|
|
117
|
+
combined; the tool watches the union of both. The config file is watched for
|
|
118
|
+
changes and reloaded automatically: add, remove, or edit targets without
|
|
119
|
+
restarting the tool (CLI-supplied targets can't change at runtime, only
|
|
120
|
+
what's in the file).
|
|
121
|
+
|
|
122
|
+
### Options
|
|
123
|
+
|
|
124
|
+
| Flag | Description |
|
|
125
|
+
| --- | --- |
|
|
126
|
+
| `-c`, `--config PATH` | YAML file listing targets; reloaded automatically on change |
|
|
127
|
+
| `TARGET` (positional) | a target to monitor: same as `-u`/`--url`, just without the flag (repeatable) |
|
|
128
|
+
| `-u`, `--url TARGET` | a target to monitor (repeatable): hostname, `hostname:port`, or a URL |
|
|
129
|
+
| `-i`, `--interval SECONDS` | seconds between check rounds (default: 30) |
|
|
130
|
+
| `--ping-timeout SECONDS` | ping reply timeout (default: 2) |
|
|
131
|
+
| `--http-timeout SECONDS` | timeout for the HTTP request or TCP connect (default: 5) |
|
|
132
|
+
| `--no-ping` | skip ICMP entirely, HTTP/TCP-only checks |
|
|
133
|
+
| `--columns LIST` | comma-separated list of table columns to show, in order (interactive UI only; `--plain`/`--json` always include every field) — see below |
|
|
134
|
+
| `--basic-auth USER:PASS` | HTTP Basic Auth for CLI-supplied HTTP targets that don't set their own `basic_auth:` in the config file — see below |
|
|
135
|
+
| `--basic-auth-env USER_VAR:PASS_VAR` | same as `--basic-auth`, but reads the username/password from the named environment variables |
|
|
136
|
+
| `--tag TAG` | tag applied to CLI-supplied targets (repeatable) — see below |
|
|
137
|
+
| `--filter-tag TAG` | only monitor targets carrying at least one of the given tags (repeatable) — see below |
|
|
138
|
+
| `--once` | run a single round and exit; exit code is `1` if any target is down, `0` otherwise. Useful as a cron/CI health gate |
|
|
139
|
+
| `--plain` | print one plain-text line per target per round instead of the live UI (for logging/piping) |
|
|
140
|
+
| `--json` | print one JSON object per target per round instead of the live UI (for machine consumption) |
|
|
141
|
+
|
|
142
|
+
`--plain` and `--json` are mutually exclusive with each other, and both skip
|
|
143
|
+
the interactive spinner/progress UI entirely.
|
|
144
|
+
|
|
145
|
+
### Note on ping
|
|
146
|
+
|
|
147
|
+
Some servers/networks (common on cloud providers, behind load balancers or
|
|
148
|
+
CDNs) block ICMP echo requests entirely even though the service itself is
|
|
149
|
+
perfectly reachable. The "Status" column already accounts for this and
|
|
150
|
+
won't flag such a target as down (unless it's ping-only, in which case ping
|
|
151
|
+
*is* the only signal there is). If you'd rather not run ping checks against
|
|
152
|
+
a target at all (e.g. to skip the ~2s ping timeout), either set `ping: false`
|
|
153
|
+
for it in the config file, or pass `--no-ping` to disable ICMP checks
|
|
154
|
+
globally. Note that a ping-only target (bare hostname, no port) can't have
|
|
155
|
+
`ping: false`, there would be nothing left to check.
|
|
156
|
+
|
|
157
|
+
### SSH availability check
|
|
158
|
+
|
|
159
|
+
`ssh://example.com` (or `ssh://example.com:1234` for a non-standard port,
|
|
160
|
+
default 22) connects over TCP and reads the server's SSH identification
|
|
161
|
+
banner (the string like `SSH-2.0-OpenSSH_9.6` that a compliant server sends
|
|
162
|
+
before any client input, per RFC 4253) rather than just checking whether
|
|
163
|
+
the port is open. If the port is open but the banner doesn't start with
|
|
164
|
+
`SSH-`, the target is reported down with `error_kind: protocol` — this
|
|
165
|
+
catches something else listening on the port. No SSH handshake, key
|
|
166
|
+
exchange, or authentication is attempted, so no credentials are needed or
|
|
167
|
+
used.
|
|
168
|
+
|
|
169
|
+
### Customizing table columns
|
|
170
|
+
|
|
171
|
+
The interactive table shows all of `target`, `status`, `ping`, `service`,
|
|
172
|
+
`latency`, `min`, `max`, `trend`, `last_online`, `last_offline`, `detail`,
|
|
173
|
+
`checked` by default, in that order. A few more are available but not
|
|
174
|
+
shown by default — `tags` (see above) and the HTTP header columns
|
|
175
|
+
`content_length`, `last_modified`, `etag`, `content_type`, `server`,
|
|
176
|
+
`header_change` (see below). To show a different subset (or reorder
|
|
177
|
+
them), pass `--columns` with a comma-separated list:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
isitup --columns target,status,detail --url https://example.com
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Or set it once in the config file:
|
|
184
|
+
|
|
185
|
+
```yaml
|
|
186
|
+
columns: [target, status, service, latency, detail]
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
`--columns` takes precedence over a config file's `columns:` list, which
|
|
190
|
+
takes precedence over the default. This only affects the interactive UI —
|
|
191
|
+
`--plain` and `--json` always include every field, since they're meant for
|
|
192
|
+
machine consumption.
|
|
193
|
+
|
|
194
|
+
### HTTP Basic Auth
|
|
195
|
+
|
|
196
|
+
Set `basic_auth` on a config file target to send an `Authorization` header
|
|
197
|
+
with its HTTP requests. Each of `username`/`password` can be given either
|
|
198
|
+
literally or via a `*_env` reference to an environment variable (but not
|
|
199
|
+
both):
|
|
200
|
+
|
|
201
|
+
```yaml
|
|
202
|
+
targets:
|
|
203
|
+
- name: Protected API
|
|
204
|
+
url: https://api.internal.example.com
|
|
205
|
+
basic_auth:
|
|
206
|
+
username: admin
|
|
207
|
+
password_env: API_PASSWORD # read from $API_PASSWORD at startup
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`basic_auth` only applies to `http://`/`https://` targets — it's rejected
|
|
211
|
+
on a bare hostname or `hostname:port` target.
|
|
212
|
+
|
|
213
|
+
For targets passed directly on the command line (positional or `--url`),
|
|
214
|
+
use `--basic-auth USER:PASS` or `--basic-auth-env USER_VAR:PASS_VAR`
|
|
215
|
+
instead. Either flag applies to every CLI-supplied HTTP target in that
|
|
216
|
+
invocation, but never to config-file targets — those always use their own
|
|
217
|
+
`basic_auth:` (or none), so the two never mix unexpectedly:
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
isitup --basic-auth-env API_USER:API_PASSWORD --url https://api.internal.example.com
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### HTTP header/metadata tracking
|
|
224
|
+
|
|
225
|
+
For HTTP(S) targets, a handful of response headers are captured every
|
|
226
|
+
round — `Content-Length` (response size), `Last-Modified`, `ETag`,
|
|
227
|
+
`Content-Type`, and `Server` — without ever touching the response body
|
|
228
|
+
itself. `--plain`/`--json` always include them (nested under `http` in
|
|
229
|
+
JSON, as `content_length=`/`last_modified=`/etc. fields in `--plain`),
|
|
230
|
+
alongside a `last_header_change`/`header_change` timestamp: the last time
|
|
231
|
+
any of those five values actually differed from the previous round (not
|
|
232
|
+
set on the very first check, since there's nothing yet to compare against).
|
|
233
|
+
This is handy for noticing "something on this page changed" without
|
|
234
|
+
diffing the body yourself.
|
|
235
|
+
|
|
236
|
+
In the interactive table these show up as the `content_length`,
|
|
237
|
+
`last_modified`, `etag`, `content_type`, `server`, and `header_change`
|
|
238
|
+
columns — available via `--columns`/config `columns:`, but not part of the
|
|
239
|
+
default set (see above), so the table's default look is unaffected.
|
|
240
|
+
|
|
241
|
+
### Tagging and grouping
|
|
242
|
+
|
|
243
|
+
Any target (ping-only, TCP, or HTTP) can carry `tags` in the config file:
|
|
244
|
+
|
|
245
|
+
```yaml
|
|
246
|
+
targets:
|
|
247
|
+
- name: Prod Website
|
|
248
|
+
url: https://example.com
|
|
249
|
+
tags: [prod, web]
|
|
250
|
+
- name: Staging Website
|
|
251
|
+
url: https://staging.example.com
|
|
252
|
+
tags: [staging, web]
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Tags show up as their own `Tags` table column — add it with `--columns
|
|
256
|
+
target,tags,status,...` (or a config `columns:` list), since it isn't in
|
|
257
|
+
the default set — and always as a `tags` field in `--plain`/`--json`
|
|
258
|
+
output.
|
|
259
|
+
|
|
260
|
+
To only monitor a subset, pass `--filter-tag` (repeatable — a target
|
|
261
|
+
matches if it has *any* of the given tags):
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
isitup --config config.yaml --filter-tag prod
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
With no `--filter-tag` given, every target is monitored, same as today.
|
|
268
|
+
For targets passed directly on the command line, `--tag TAG` (repeatable)
|
|
269
|
+
tags every CLI-supplied target in that invocation — like `--basic-auth`,
|
|
270
|
+
it never touches config-file targets, which set their own `tags:` instead.
|
|
271
|
+
|
|
272
|
+
### Failure classification
|
|
273
|
+
|
|
274
|
+
When the HTTP, TCP, or SSH probe fails, the specific reason is captured as
|
|
275
|
+
`error_kind` (visible in `--json` output, and shown as a short tag like
|
|
276
|
+
`DOWN (DNS)` in the Service column otherwise):
|
|
277
|
+
|
|
278
|
+
| `error_kind` | Meaning |
|
|
279
|
+
| --- | --- |
|
|
280
|
+
| `dns` | the hostname failed to resolve |
|
|
281
|
+
| `tls` | a TLS/certificate error (HTTP only, e.g. expired or self-signed cert) |
|
|
282
|
+
| `redirect` | too many redirects (HTTP only, possible redirect loop) |
|
|
283
|
+
| `timeout` | didn't complete within `--http-timeout` |
|
|
284
|
+
| `connection` | connection refused or otherwise unreachable |
|
|
285
|
+
| `protocol` | connected, but the server's response wasn't a valid SSH banner (SSH only) |
|
|
286
|
+
| `client_error` / `server_error` | got a 4xx / 5xx HTTP response |
|
|
287
|
+
| `request_error` | anything else `requests` raised |
|
|
288
|
+
|
|
289
|
+
## Development
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
uv sync
|
|
293
|
+
uv run isitup --once --url https://example.com
|
|
294
|
+
|
|
295
|
+
uv run pytest # tests
|
|
296
|
+
uv run ruff check . # lint
|
|
297
|
+
uv run ruff format . # format
|
|
298
|
+
uv run mypy src tests # type check
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Every push and pull request runs this same lint/type-check/test suite via
|
|
302
|
+
[GitHub Actions](.github/workflows/ci.yml), across Linux and Windows.
|
|
303
|
+
|
|
304
|
+
## Changelog
|
|
305
|
+
|
|
306
|
+
See [CHANGELOG.md](CHANGELOG.md).
|
|
307
|
+
|
|
308
|
+
## License
|
|
309
|
+
|
|
310
|
+
[GPL-3.0](LICENSE): see [LICENSE](LICENSE) for the full text.
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/ramsesoriginal/isitup/main/logo/main_hero.svg" alt="isitup" width="720">
|
|
3
|
+
|
|
4
|
+
<p><strong>Is it up? Ping, HTTP, and TCP checks in one small, fast CLI.</strong></p>
|
|
5
|
+
|
|
6
|
+
[](https://pypi.org/project/isitup-cli/)
|
|
7
|
+
[](https://github.com/ramsesoriginal/isitup/actions/workflows/ci.yml)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[](pyproject.toml)
|
|
10
|
+
[](https://github.com/astral-sh/uv)
|
|
11
|
+
[](https://github.com/astral-sh/ruff)
|
|
12
|
+
[](https://mypy-lang.org/)
|
|
13
|
+
[](https://github.com/ramsesoriginal/isitup)
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
**isitup** is a small CLI that periodically checks whether servers respond to ping and,
|
|
19
|
+
depending on how the target is written, HTTP, a TCP port, or SSH:
|
|
20
|
+
|
|
21
|
+
| Target | Checks |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| `example.com` | ping only |
|
|
24
|
+
| `example.com:1337` | ping + TCP connect to port 1337 |
|
|
25
|
+
| `http://example.com` / `https://example.com` | ping + HTTP (catching a 500, a DNS failure, an expired TLS certificate, a redirect loop, etc.) |
|
|
26
|
+
| `https://example.com:1337` | ping + HTTP, against that port |
|
|
27
|
+
| `ssh://example.com` / `ssh://example.com:1337` | ping + SSH (connects and verifies the server's SSH identification banner; defaults to port 22) |
|
|
28
|
+
|
|
29
|
+
Ping is checked independently from the HTTP/TCP/SSH probe: many hosts (behind
|
|
30
|
+
load balancers, CDNs, or firewalls) drop ICMP but serve their actual service
|
|
31
|
+
just fine, so a blocked ping alone doesn't mark a target down: the "Status"
|
|
32
|
+
column is based on the probe, with ping shown alongside as extra diagnostic
|
|
33
|
+
info. A target only shows as `OFFLINE` when both ping and the probe fail; a
|
|
34
|
+
ping-only target's status is based on ping alone, since there's nothing else
|
|
35
|
+
to check.
|
|
36
|
+
|
|
37
|
+
If the same host appears more than once (e.g. `example.com` and
|
|
38
|
+
`https://example.com/health`), it's only pinged once per round. The ping
|
|
39
|
+
result is shared across all targets pointing at that host.
|
|
40
|
+
|
|
41
|
+
While checks are running you get a live spinner and a progress bar per
|
|
42
|
+
target counting down its timeout; results are then shown in a table that
|
|
43
|
+
updates in place (via [`rich`](https://github.com/Textualize/rich)) rather
|
|
44
|
+
than reprinting on every round.
|
|
45
|
+
|
|
46
|
+
Each target also keeps a rolling window of its last 20 response times for
|
|
47
|
+
the session (HTTP response time, TCP connect time, or SSH connect+banner
|
|
48
|
+
time, whichever applies), shown as Min/Max latency columns and a tiny
|
|
49
|
+
sparkline ("Trend"), along with
|
|
50
|
+
the last time it was seen online and the last time it was seen offline.
|
|
51
|
+
This history is in-memory only and resets each time you start the tool.
|
|
52
|
+
|
|
53
|
+
## Installation
|
|
54
|
+
|
|
55
|
+
The package is published on PyPI as `isitup-cli`, but it installs a plain
|
|
56
|
+
`isitup` command:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
uv tool install isitup-cli
|
|
60
|
+
# or: pipx install isitup-cli
|
|
61
|
+
# or: pip install isitup-cli
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
To try unreleased changes straight from `main` instead (no PyPI release
|
|
65
|
+
needed):
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
uv tool install git+https://github.com/ramsesoriginal/isitup
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Either way, remove it later with `uv tool uninstall isitup`.
|
|
72
|
+
|
|
73
|
+
## Usage
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
isitup example.com https://example.org db.internal:5432
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`--url`/`-u` work the same way and can be freely mixed with positional
|
|
80
|
+
targets: `isitup example.com -u https://example.org` is the same as
|
|
81
|
+
`isitup example.com https://example.org`:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
isitup --url example.com --url https://example.org --url db.internal:5432
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Or with a config file:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
cp config.example.yaml config.yaml
|
|
91
|
+
# edit config.yaml
|
|
92
|
+
isitup --config config.yaml
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Config files and CLI-supplied targets (positional or `--url`) can be
|
|
96
|
+
combined; the tool watches the union of both. The config file is watched for
|
|
97
|
+
changes and reloaded automatically: add, remove, or edit targets without
|
|
98
|
+
restarting the tool (CLI-supplied targets can't change at runtime, only
|
|
99
|
+
what's in the file).
|
|
100
|
+
|
|
101
|
+
### Options
|
|
102
|
+
|
|
103
|
+
| Flag | Description |
|
|
104
|
+
| --- | --- |
|
|
105
|
+
| `-c`, `--config PATH` | YAML file listing targets; reloaded automatically on change |
|
|
106
|
+
| `TARGET` (positional) | a target to monitor: same as `-u`/`--url`, just without the flag (repeatable) |
|
|
107
|
+
| `-u`, `--url TARGET` | a target to monitor (repeatable): hostname, `hostname:port`, or a URL |
|
|
108
|
+
| `-i`, `--interval SECONDS` | seconds between check rounds (default: 30) |
|
|
109
|
+
| `--ping-timeout SECONDS` | ping reply timeout (default: 2) |
|
|
110
|
+
| `--http-timeout SECONDS` | timeout for the HTTP request or TCP connect (default: 5) |
|
|
111
|
+
| `--no-ping` | skip ICMP entirely, HTTP/TCP-only checks |
|
|
112
|
+
| `--columns LIST` | comma-separated list of table columns to show, in order (interactive UI only; `--plain`/`--json` always include every field) — see below |
|
|
113
|
+
| `--basic-auth USER:PASS` | HTTP Basic Auth for CLI-supplied HTTP targets that don't set their own `basic_auth:` in the config file — see below |
|
|
114
|
+
| `--basic-auth-env USER_VAR:PASS_VAR` | same as `--basic-auth`, but reads the username/password from the named environment variables |
|
|
115
|
+
| `--tag TAG` | tag applied to CLI-supplied targets (repeatable) — see below |
|
|
116
|
+
| `--filter-tag TAG` | only monitor targets carrying at least one of the given tags (repeatable) — see below |
|
|
117
|
+
| `--once` | run a single round and exit; exit code is `1` if any target is down, `0` otherwise. Useful as a cron/CI health gate |
|
|
118
|
+
| `--plain` | print one plain-text line per target per round instead of the live UI (for logging/piping) |
|
|
119
|
+
| `--json` | print one JSON object per target per round instead of the live UI (for machine consumption) |
|
|
120
|
+
|
|
121
|
+
`--plain` and `--json` are mutually exclusive with each other, and both skip
|
|
122
|
+
the interactive spinner/progress UI entirely.
|
|
123
|
+
|
|
124
|
+
### Note on ping
|
|
125
|
+
|
|
126
|
+
Some servers/networks (common on cloud providers, behind load balancers or
|
|
127
|
+
CDNs) block ICMP echo requests entirely even though the service itself is
|
|
128
|
+
perfectly reachable. The "Status" column already accounts for this and
|
|
129
|
+
won't flag such a target as down (unless it's ping-only, in which case ping
|
|
130
|
+
*is* the only signal there is). If you'd rather not run ping checks against
|
|
131
|
+
a target at all (e.g. to skip the ~2s ping timeout), either set `ping: false`
|
|
132
|
+
for it in the config file, or pass `--no-ping` to disable ICMP checks
|
|
133
|
+
globally. Note that a ping-only target (bare hostname, no port) can't have
|
|
134
|
+
`ping: false`, there would be nothing left to check.
|
|
135
|
+
|
|
136
|
+
### SSH availability check
|
|
137
|
+
|
|
138
|
+
`ssh://example.com` (or `ssh://example.com:1234` for a non-standard port,
|
|
139
|
+
default 22) connects over TCP and reads the server's SSH identification
|
|
140
|
+
banner (the string like `SSH-2.0-OpenSSH_9.6` that a compliant server sends
|
|
141
|
+
before any client input, per RFC 4253) rather than just checking whether
|
|
142
|
+
the port is open. If the port is open but the banner doesn't start with
|
|
143
|
+
`SSH-`, the target is reported down with `error_kind: protocol` — this
|
|
144
|
+
catches something else listening on the port. No SSH handshake, key
|
|
145
|
+
exchange, or authentication is attempted, so no credentials are needed or
|
|
146
|
+
used.
|
|
147
|
+
|
|
148
|
+
### Customizing table columns
|
|
149
|
+
|
|
150
|
+
The interactive table shows all of `target`, `status`, `ping`, `service`,
|
|
151
|
+
`latency`, `min`, `max`, `trend`, `last_online`, `last_offline`, `detail`,
|
|
152
|
+
`checked` by default, in that order. A few more are available but not
|
|
153
|
+
shown by default — `tags` (see above) and the HTTP header columns
|
|
154
|
+
`content_length`, `last_modified`, `etag`, `content_type`, `server`,
|
|
155
|
+
`header_change` (see below). To show a different subset (or reorder
|
|
156
|
+
them), pass `--columns` with a comma-separated list:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
isitup --columns target,status,detail --url https://example.com
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Or set it once in the config file:
|
|
163
|
+
|
|
164
|
+
```yaml
|
|
165
|
+
columns: [target, status, service, latency, detail]
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
`--columns` takes precedence over a config file's `columns:` list, which
|
|
169
|
+
takes precedence over the default. This only affects the interactive UI —
|
|
170
|
+
`--plain` and `--json` always include every field, since they're meant for
|
|
171
|
+
machine consumption.
|
|
172
|
+
|
|
173
|
+
### HTTP Basic Auth
|
|
174
|
+
|
|
175
|
+
Set `basic_auth` on a config file target to send an `Authorization` header
|
|
176
|
+
with its HTTP requests. Each of `username`/`password` can be given either
|
|
177
|
+
literally or via a `*_env` reference to an environment variable (but not
|
|
178
|
+
both):
|
|
179
|
+
|
|
180
|
+
```yaml
|
|
181
|
+
targets:
|
|
182
|
+
- name: Protected API
|
|
183
|
+
url: https://api.internal.example.com
|
|
184
|
+
basic_auth:
|
|
185
|
+
username: admin
|
|
186
|
+
password_env: API_PASSWORD # read from $API_PASSWORD at startup
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
`basic_auth` only applies to `http://`/`https://` targets — it's rejected
|
|
190
|
+
on a bare hostname or `hostname:port` target.
|
|
191
|
+
|
|
192
|
+
For targets passed directly on the command line (positional or `--url`),
|
|
193
|
+
use `--basic-auth USER:PASS` or `--basic-auth-env USER_VAR:PASS_VAR`
|
|
194
|
+
instead. Either flag applies to every CLI-supplied HTTP target in that
|
|
195
|
+
invocation, but never to config-file targets — those always use their own
|
|
196
|
+
`basic_auth:` (or none), so the two never mix unexpectedly:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
isitup --basic-auth-env API_USER:API_PASSWORD --url https://api.internal.example.com
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### HTTP header/metadata tracking
|
|
203
|
+
|
|
204
|
+
For HTTP(S) targets, a handful of response headers are captured every
|
|
205
|
+
round — `Content-Length` (response size), `Last-Modified`, `ETag`,
|
|
206
|
+
`Content-Type`, and `Server` — without ever touching the response body
|
|
207
|
+
itself. `--plain`/`--json` always include them (nested under `http` in
|
|
208
|
+
JSON, as `content_length=`/`last_modified=`/etc. fields in `--plain`),
|
|
209
|
+
alongside a `last_header_change`/`header_change` timestamp: the last time
|
|
210
|
+
any of those five values actually differed from the previous round (not
|
|
211
|
+
set on the very first check, since there's nothing yet to compare against).
|
|
212
|
+
This is handy for noticing "something on this page changed" without
|
|
213
|
+
diffing the body yourself.
|
|
214
|
+
|
|
215
|
+
In the interactive table these show up as the `content_length`,
|
|
216
|
+
`last_modified`, `etag`, `content_type`, `server`, and `header_change`
|
|
217
|
+
columns — available via `--columns`/config `columns:`, but not part of the
|
|
218
|
+
default set (see above), so the table's default look is unaffected.
|
|
219
|
+
|
|
220
|
+
### Tagging and grouping
|
|
221
|
+
|
|
222
|
+
Any target (ping-only, TCP, or HTTP) can carry `tags` in the config file:
|
|
223
|
+
|
|
224
|
+
```yaml
|
|
225
|
+
targets:
|
|
226
|
+
- name: Prod Website
|
|
227
|
+
url: https://example.com
|
|
228
|
+
tags: [prod, web]
|
|
229
|
+
- name: Staging Website
|
|
230
|
+
url: https://staging.example.com
|
|
231
|
+
tags: [staging, web]
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Tags show up as their own `Tags` table column — add it with `--columns
|
|
235
|
+
target,tags,status,...` (or a config `columns:` list), since it isn't in
|
|
236
|
+
the default set — and always as a `tags` field in `--plain`/`--json`
|
|
237
|
+
output.
|
|
238
|
+
|
|
239
|
+
To only monitor a subset, pass `--filter-tag` (repeatable — a target
|
|
240
|
+
matches if it has *any* of the given tags):
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
isitup --config config.yaml --filter-tag prod
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
With no `--filter-tag` given, every target is monitored, same as today.
|
|
247
|
+
For targets passed directly on the command line, `--tag TAG` (repeatable)
|
|
248
|
+
tags every CLI-supplied target in that invocation — like `--basic-auth`,
|
|
249
|
+
it never touches config-file targets, which set their own `tags:` instead.
|
|
250
|
+
|
|
251
|
+
### Failure classification
|
|
252
|
+
|
|
253
|
+
When the HTTP, TCP, or SSH probe fails, the specific reason is captured as
|
|
254
|
+
`error_kind` (visible in `--json` output, and shown as a short tag like
|
|
255
|
+
`DOWN (DNS)` in the Service column otherwise):
|
|
256
|
+
|
|
257
|
+
| `error_kind` | Meaning |
|
|
258
|
+
| --- | --- |
|
|
259
|
+
| `dns` | the hostname failed to resolve |
|
|
260
|
+
| `tls` | a TLS/certificate error (HTTP only, e.g. expired or self-signed cert) |
|
|
261
|
+
| `redirect` | too many redirects (HTTP only, possible redirect loop) |
|
|
262
|
+
| `timeout` | didn't complete within `--http-timeout` |
|
|
263
|
+
| `connection` | connection refused or otherwise unreachable |
|
|
264
|
+
| `protocol` | connected, but the server's response wasn't a valid SSH banner (SSH only) |
|
|
265
|
+
| `client_error` / `server_error` | got a 4xx / 5xx HTTP response |
|
|
266
|
+
| `request_error` | anything else `requests` raised |
|
|
267
|
+
|
|
268
|
+
## Development
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
uv sync
|
|
272
|
+
uv run isitup --once --url https://example.com
|
|
273
|
+
|
|
274
|
+
uv run pytest # tests
|
|
275
|
+
uv run ruff check . # lint
|
|
276
|
+
uv run ruff format . # format
|
|
277
|
+
uv run mypy src tests # type check
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Every push and pull request runs this same lint/type-check/test suite via
|
|
281
|
+
[GitHub Actions](.github/workflows/ci.yml), across Linux and Windows.
|
|
282
|
+
|
|
283
|
+
## Changelog
|
|
284
|
+
|
|
285
|
+
See [CHANGELOG.md](CHANGELOG.md).
|
|
286
|
+
|
|
287
|
+
## License
|
|
288
|
+
|
|
289
|
+
[GPL-3.0](LICENSE): see [LICENSE](LICENSE) for the full text.
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "isitup-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "A small CLI that periodically checks whether servers respond to ping, HTTP, and TCP ports"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "GPL-3.0-only"
|
|
7
7
|
license-files = ["LICENSE"]
|
|
8
8
|
requires-python = ">=3.14"
|
|
9
9
|
classifiers = [
|
|
10
|
+
"Development Status :: 4 - Beta",
|
|
10
11
|
"Environment :: Console",
|
|
11
12
|
"Intended Audience :: System Administrators",
|
|
12
13
|
"Topic :: System :: Networking :: Monitoring",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "isitup-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "A small CLI that periodically checks whether servers respond to ping, HTTP, and TCP ports"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "GPL-3.0-only"
|
|
@@ -10,6 +10,7 @@ authors = [
|
|
|
10
10
|
]
|
|
11
11
|
requires-python = ">=3.14"
|
|
12
12
|
classifiers = [
|
|
13
|
+
"Development Status :: 4 - Beta",
|
|
13
14
|
"Environment :: Console",
|
|
14
15
|
"Intended Audience :: System Administrators",
|
|
15
16
|
"Topic :: System :: Networking :: Monitoring",
|