eping 3.85__tar.gz → 3.91__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.
- {eping-3.85 → eping-3.91}/PKG-INFO +11 -4
- {eping-3.85 → eping-3.91}/README.md +10 -3
- {eping-3.85 → eping-3.91}/pyproject.toml +1 -1
- {eping-3.85 → eping-3.91}/src/eping/eping.py +264 -6
- {eping-3.85 → eping-3.91}/src/eping.egg-info/PKG-INFO +11 -4
- {eping-3.85 → eping-3.91}/LICENSE +0 -0
- {eping-3.85 → eping-3.91}/setup.cfg +0 -0
- {eping-3.85 → eping-3.91}/src/eping/__init__.py +0 -0
- {eping-3.85 → eping-3.91}/src/eping/_launch_eping.py +0 -0
- {eping-3.85 → eping-3.91}/src/eping/epinga.py +0 -0
- {eping-3.85 → eping-3.91}/src/eping.egg-info/SOURCES.txt +0 -0
- {eping-3.85 → eping-3.91}/src/eping.egg-info/dependency_links.txt +0 -0
- {eping-3.85 → eping-3.91}/src/eping.egg-info/entry_points.txt +0 -0
- {eping-3.85 → eping-3.91}/src/eping.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: eping
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.91
|
|
4
4
|
Summary: Continuous ICMP reachability monitor built on fping, with a CSV logfile analyser (epinga)
|
|
5
5
|
Author-email: Ewald Jeitler <ewald@jeitler.cc>
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -17,7 +17,7 @@ Description-Content-Type: text/markdown
|
|
|
17
17
|
License-File: LICENSE
|
|
18
18
|
Dynamic: license-file
|
|
19
19
|
|
|
20
|
-
# eping.py 3.
|
|
20
|
+
# eping.py 3.91
|
|
21
21
|
|
|
22
22
|
Continuous ICMP reachability monitor built on `fping`. Scans a host list in a loop,
|
|
23
23
|
reports each host UP/DOWN/NO-DNS, counts state changes. CLI (curses) or web GUI.
|
|
@@ -244,7 +244,7 @@ Single self-contained page, no external resources.
|
|
|
244
244
|
- Row 1: view select (9 views), sort order select, SET REFERENCE, ZERO CHANGES,
|
|
245
245
|
CLEAR ALL, address mode select, GET NAMES, ADV OPTIONS, RESET LOG, FILE OPERATIONS
|
|
246
246
|
select, EXIT, font size.
|
|
247
|
-
- Row 2: match filter (SET/CLEAR), ADV FILTER, host field (ADD/DELETE), comment
|
|
247
|
+
- Row 2: match filter (SET/CLEAR), ADV FILTER, host field (ADD/DELETE), ADD/DEL LIST, comment
|
|
248
248
|
field (COMMENT), GENERATE REPORT select.
|
|
249
249
|
- View select, sort order select, address mode select and ADV OPTIONS all match the
|
|
250
250
|
CLI's `U`/`O`/`P`/`X` pickers exactly. FILE OPERATIONS (incl. ADD HOSTS FILE FROM CLIENT/
|
|
@@ -254,7 +254,7 @@ Single self-contained page, no external resources.
|
|
|
254
254
|
| Entry | Does |
|
|
255
255
|
|---|---|
|
|
256
256
|
| ADD HOSTS FILE FROM SERVER | picks one or more `.txt` files or inventory `.csv` files already on the server, adds their hosts; a `.csv` without the inventory header, e.g. an eping log, is refused |
|
|
257
|
-
| ADD HOSTS FILE FROM CLIENT |
|
|
257
|
+
| ADD HOSTS FILE FROM CLIENT | dialog with a file picker (`.txt`/`.csv`; works in Safari), adds the hosts; a file can also be dropped anywhere on the page |
|
|
258
258
|
| SAVE HOSTS FILE ON SERVER | confirmation, then saves the currently displayed hosts; blank name overwrites the active `-f` hostfile (first one, if eping.py was started with one) or, with none active, the default `eping-hosts.txt`; a typed name always saves as that new file instead, `.txt` appended once, active hostfile left untouched. If the displayed hosts carry inventory values (CSV import) an inventory `.csv` is written instead (`;` delimited, columns as in *Inventory CSV import*); a `.txt` name or hostfile then becomes `.csv`, the default is `eping-hosts.csv`. The options that differ from the default are written first as `opt:` lines (see *Host file format*); for a `.csv` the checkboxes `include options (opt: lines)` (default on) and `save as .txt (hosts only, no inventory)` (default off) are offered |
|
|
259
259
|
| UPLOAD FILE TO SERVER | picks a local `.txt`/`.csv`, saves as-is server-side, no host parsing; never overwrites, appends `-1`/`-2`/... |
|
|
260
260
|
| DOWNLOAD ALL HOSTS | full reference list: inventory `.csv` if the hosts carry inventory values, else `.txt`; non-default options first as `opt:` lines (`.csv`: checkboxes `include options`, default on, and `save as .txt`, default off) |
|
|
@@ -273,6 +273,13 @@ Single self-contained page, no external resources.
|
|
|
273
273
|
`+`/`−` font size (works while typing). No keyboard shortcut for `X`/ADV OPTIONS or
|
|
274
274
|
GENERATE REPORT - click required.
|
|
275
275
|
- Host field feeds both ADD/DELETE; ENTER triggers the button used last, ESC clears.
|
|
276
|
+
- **ADD/DEL LIST** (button after DELETE; also Ctrl/Cmd+V on the page while no field has focus): paste a
|
|
277
|
+
host list - one per line or separated by comma/semicolon/blank, `#` comment, CIDR expanded. A live
|
|
278
|
+
preview shows found / new / already in list / ignored words / masks out of range; ADD and DELETE are
|
|
279
|
+
enabled only if they would change something (DELETE over 20 hosts asks to confirm; exact name match
|
|
280
|
+
only). A pasted inventory `.csv` (with the `IP Address`/`Hostname` header) also updates hosts already in
|
|
281
|
+
the list: IP entries (e.g. from a `.txt`) are renamed to the CSV hostname, history kept, and the CSV
|
|
282
|
+
values are attached. Works with `-wvc` too.
|
|
276
283
|
- Files can also be dropped anywhere on the page (max 16MB, see *Host file format*).
|
|
277
284
|
- Commands acknowledge immediately; a click aborts the running round.
|
|
278
285
|
- **Host popup**: hovering a host row (after ~350 ms) shows everything known about it:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# eping.py 3.
|
|
1
|
+
# eping.py 3.91
|
|
2
2
|
|
|
3
3
|
Continuous ICMP reachability monitor built on `fping`. Scans a host list in a loop,
|
|
4
4
|
reports each host UP/DOWN/NO-DNS, counts state changes. CLI (curses) or web GUI.
|
|
@@ -225,7 +225,7 @@ Single self-contained page, no external resources.
|
|
|
225
225
|
- Row 1: view select (9 views), sort order select, SET REFERENCE, ZERO CHANGES,
|
|
226
226
|
CLEAR ALL, address mode select, GET NAMES, ADV OPTIONS, RESET LOG, FILE OPERATIONS
|
|
227
227
|
select, EXIT, font size.
|
|
228
|
-
- Row 2: match filter (SET/CLEAR), ADV FILTER, host field (ADD/DELETE), comment
|
|
228
|
+
- Row 2: match filter (SET/CLEAR), ADV FILTER, host field (ADD/DELETE), ADD/DEL LIST, comment
|
|
229
229
|
field (COMMENT), GENERATE REPORT select.
|
|
230
230
|
- View select, sort order select, address mode select and ADV OPTIONS all match the
|
|
231
231
|
CLI's `U`/`O`/`P`/`X` pickers exactly. FILE OPERATIONS (incl. ADD HOSTS FILE FROM CLIENT/
|
|
@@ -235,7 +235,7 @@ Single self-contained page, no external resources.
|
|
|
235
235
|
| Entry | Does |
|
|
236
236
|
|---|---|
|
|
237
237
|
| ADD HOSTS FILE FROM SERVER | picks one or more `.txt` files or inventory `.csv` files already on the server, adds their hosts; a `.csv` without the inventory header, e.g. an eping log, is refused |
|
|
238
|
-
| ADD HOSTS FILE FROM CLIENT |
|
|
238
|
+
| ADD HOSTS FILE FROM CLIENT | dialog with a file picker (`.txt`/`.csv`; works in Safari), adds the hosts; a file can also be dropped anywhere on the page |
|
|
239
239
|
| SAVE HOSTS FILE ON SERVER | confirmation, then saves the currently displayed hosts; blank name overwrites the active `-f` hostfile (first one, if eping.py was started with one) or, with none active, the default `eping-hosts.txt`; a typed name always saves as that new file instead, `.txt` appended once, active hostfile left untouched. If the displayed hosts carry inventory values (CSV import) an inventory `.csv` is written instead (`;` delimited, columns as in *Inventory CSV import*); a `.txt` name or hostfile then becomes `.csv`, the default is `eping-hosts.csv`. The options that differ from the default are written first as `opt:` lines (see *Host file format*); for a `.csv` the checkboxes `include options (opt: lines)` (default on) and `save as .txt (hosts only, no inventory)` (default off) are offered |
|
|
240
240
|
| UPLOAD FILE TO SERVER | picks a local `.txt`/`.csv`, saves as-is server-side, no host parsing; never overwrites, appends `-1`/`-2`/... |
|
|
241
241
|
| DOWNLOAD ALL HOSTS | full reference list: inventory `.csv` if the hosts carry inventory values, else `.txt`; non-default options first as `opt:` lines (`.csv`: checkboxes `include options`, default on, and `save as .txt`, default off) |
|
|
@@ -254,6 +254,13 @@ Single self-contained page, no external resources.
|
|
|
254
254
|
`+`/`−` font size (works while typing). No keyboard shortcut for `X`/ADV OPTIONS or
|
|
255
255
|
GENERATE REPORT - click required.
|
|
256
256
|
- Host field feeds both ADD/DELETE; ENTER triggers the button used last, ESC clears.
|
|
257
|
+
- **ADD/DEL LIST** (button after DELETE; also Ctrl/Cmd+V on the page while no field has focus): paste a
|
|
258
|
+
host list - one per line or separated by comma/semicolon/blank, `#` comment, CIDR expanded. A live
|
|
259
|
+
preview shows found / new / already in list / ignored words / masks out of range; ADD and DELETE are
|
|
260
|
+
enabled only if they would change something (DELETE over 20 hosts asks to confirm; exact name match
|
|
261
|
+
only). A pasted inventory `.csv` (with the `IP Address`/`Hostname` header) also updates hosts already in
|
|
262
|
+
the list: IP entries (e.g. from a `.txt`) are renamed to the CSV hostname, history kept, and the CSV
|
|
263
|
+
values are attached. Works with `-wvc` too.
|
|
257
264
|
- Files can also be dropped anywhere on the page (max 16MB, see *Host file format*).
|
|
258
265
|
- Commands acknowledge immediately; a click aborts the running round.
|
|
259
266
|
- **Host popup**: hovering a host row (after ~350 ms) shows everything known about it:
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "eping"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.91"
|
|
8
8
|
description = "Continuous ICMP reachability monitor built on fping, with a CSV logfile analyser (epinga)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "GPL-3.0-or-later" }
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# I knew how it worked.
|
|
8
8
|
# Now, only god knows it!
|
|
9
9
|
# - - - - - - - - - - - - - - - - - - - - - - - -
|
|
10
|
-
VERSION = '3.
|
|
10
|
+
VERSION = '3.91'
|
|
11
11
|
version = VERSION # legacy alias (kept for existing references)
|
|
12
12
|
|
|
13
13
|
# --- scaling limits ---
|
|
@@ -710,6 +710,26 @@ def _rename_host_in_place(old, new, original_hosts_list, active_hosts_list,
|
|
|
710
710
|
host_csv_ip.setdefault(new, host_csv_ip[old])
|
|
711
711
|
forget_names([new]) # force a fresh forward lookup for the new identity
|
|
712
712
|
|
|
713
|
+
def merge_csv_ips_into_lists(hosts, original_hosts_list, active_hosts_list,
|
|
714
|
+
host_state, up_seen, down_streak):
|
|
715
|
+
"""ADD LIST with an inventory CSV: a CSV host whose IP is already in the lists as a
|
|
716
|
+
plain IP entry (e.g. from a .txt file) updates that entry - it is renamed to the
|
|
717
|
+
CSV hostname in place (history kept) instead of being added a second time.
|
|
718
|
+
Returns the number of renamed entries. host_csv_ip holds the IP of each CSV host."""
|
|
719
|
+
cur = set(original_hosts_list) | set(active_hosts_list)
|
|
720
|
+
renamed = 0
|
|
721
|
+
for h in hosts:
|
|
722
|
+
if h in cur:
|
|
723
|
+
continue
|
|
724
|
+
ip = host_csv_ip.get(h)
|
|
725
|
+
if ip and ip != h and ip in cur:
|
|
726
|
+
_rename_host_in_place(ip, h, original_hosts_list, active_hosts_list,
|
|
727
|
+
host_state, up_seen, down_streak)
|
|
728
|
+
cur.discard(ip)
|
|
729
|
+
cur.add(h)
|
|
730
|
+
renamed += 1
|
|
731
|
+
return renamed
|
|
732
|
+
|
|
713
733
|
def _forward_confirms(name, ip):
|
|
714
734
|
"""True if 'name' has a forward record (A for an IPv4 ip, AAAA for an IPv6 ip)
|
|
715
735
|
that resolves back to exactly this ip. Guards GET NAMES: a PTR record with no
|
|
@@ -1768,7 +1788,7 @@ def parse_hosts_from_text(text, stats=None, use_names=False, name=None):
|
|
|
1768
1788
|
recognised by its header; anything else - and every file named *.txt - is parsed
|
|
1769
1789
|
as a plain host list (_parse_plain_hosts). A NUL byte separates concatenated files."""
|
|
1770
1790
|
csv_ok = not (name and str(name).lower().endswith('.txt'))
|
|
1771
|
-
out, seen, networks, skipped = [], set(), 0, []
|
|
1791
|
+
out, seen, networks, skipped, ignored = [], set(), 0, [], []
|
|
1772
1792
|
csv_stats = {}
|
|
1773
1793
|
for seg in (text or '').split('\x00'):
|
|
1774
1794
|
sub = {}
|
|
@@ -1781,6 +1801,7 @@ def parse_hosts_from_text(text, stats=None, use_names=False, name=None):
|
|
|
1781
1801
|
csv_stats['csv'] = True
|
|
1782
1802
|
networks += sub.get('networks', 0)
|
|
1783
1803
|
skipped.extend(sub.get('skipped', []))
|
|
1804
|
+
ignored.extend(sub.get('ignored', []))
|
|
1784
1805
|
for h in got:
|
|
1785
1806
|
if h not in seen:
|
|
1786
1807
|
seen.add(h)
|
|
@@ -1788,6 +1809,7 @@ def parse_hosts_from_text(text, stats=None, use_names=False, name=None):
|
|
|
1788
1809
|
if stats is not None:
|
|
1789
1810
|
stats['networks'] = networks
|
|
1790
1811
|
stats['skipped'] = skipped
|
|
1812
|
+
stats['ignored'] = ignored
|
|
1791
1813
|
stats.update(csv_stats)
|
|
1792
1814
|
return out
|
|
1793
1815
|
|
|
@@ -1804,6 +1826,7 @@ def _parse_plain_hosts(text, stats=None):
|
|
|
1804
1826
|
fqdns = []
|
|
1805
1827
|
networks = 0
|
|
1806
1828
|
skipped = []
|
|
1829
|
+
ignored = []
|
|
1807
1830
|
for line in (text or '').splitlines():
|
|
1808
1831
|
line = line.split('#')[0] # ignore comments
|
|
1809
1832
|
for word in line.replace(',', ' ').replace(';', ' ').split():
|
|
@@ -1823,6 +1846,8 @@ def _parse_plain_hosts(text, stats=None):
|
|
|
1823
1846
|
ips.append(normalize_ip(word.rsplit('/', 1)[0])) # single-host IPv6 CIDR, no expansion
|
|
1824
1847
|
elif match_re(word, fqdn_re):
|
|
1825
1848
|
fqdns.append(word)
|
|
1849
|
+
else:
|
|
1850
|
+
ignored.append(word)
|
|
1826
1851
|
seen = set()
|
|
1827
1852
|
out = []
|
|
1828
1853
|
for h in ips + fqdns:
|
|
@@ -1832,6 +1857,7 @@ def _parse_plain_hosts(text, stats=None):
|
|
|
1832
1857
|
if stats is not None:
|
|
1833
1858
|
stats['networks'] = networks
|
|
1834
1859
|
stats['skipped'] = skipped
|
|
1860
|
+
stats['ignored'] = ignored
|
|
1835
1861
|
return out
|
|
1836
1862
|
|
|
1837
1863
|
def extract_opt_lines(text):
|
|
@@ -2848,6 +2874,29 @@ WEB_INDEX_HTML = r"""<!DOCTYPE html>
|
|
|
2848
2874
|
</div>
|
|
2849
2875
|
</div>
|
|
2850
2876
|
</div>
|
|
2877
|
+
<div id="hostListModal" class="modal-overlay" style="display:none">
|
|
2878
|
+
<div class="modal-box wide">
|
|
2879
|
+
<h3>ADD / DELETE HOST LIST</h3>
|
|
2880
|
+
<p>Paste hosts: IPv4/IPv6, hostname, IPv4 CIDR - separated by new line, comma, semicolon or blank; `#` starts a comment.</p>
|
|
2881
|
+
<textarea id="hostListText" rows="10" spellcheck="false" style="width:100%;box-sizing:border-box;margin-bottom:8px;background:var(--panel);color:var(--fg);border:1px solid var(--ctrl-line);border-radius:4px;padding:6px 8px;font-family:inherit;font-size:12px;resize:vertical"></textarea>
|
|
2882
|
+
<div id="hostListInfo" style="min-height:34px;margin-bottom:12px;font-size:12px;color:var(--dim)"> </div>
|
|
2883
|
+
<div class="modal-buttons">
|
|
2884
|
+
<button id="hostListAddBtn" disabled>ADD</button>
|
|
2885
|
+
<button id="hostListDelBtn" class="danger" disabled>DELETE</button>
|
|
2886
|
+
<button id="hostListCancelBtn">CANCEL</button>
|
|
2887
|
+
</div>
|
|
2888
|
+
</div>
|
|
2889
|
+
</div>
|
|
2890
|
+
<div id="addFromClientModal" class="modal-overlay" style="display:none">
|
|
2891
|
+
<div class="modal-box">
|
|
2892
|
+
<h3>ADD FROM CLIENT</h3>
|
|
2893
|
+
<p>Choose a `.txt` or `.csv` hosts file from this computer to add its hosts.</p>
|
|
2894
|
+
<input type="file" id="fileInputVis" accept=".txt,.csv,.list,text/plain" style="margin-bottom:14px;color:var(--fg)">
|
|
2895
|
+
<div class="modal-buttons">
|
|
2896
|
+
<button id="modalBtnAddFromClientCancel">CANCEL</button>
|
|
2897
|
+
</div>
|
|
2898
|
+
</div>
|
|
2899
|
+
</div>
|
|
2851
2900
|
<div id="chooseReportModal" class="modal-overlay" style="display:none">
|
|
2852
2901
|
<div class="modal-box wide xwide">
|
|
2853
2902
|
<h3>CHOOSE LOGFILE</h3>
|
|
@@ -2996,6 +3045,7 @@ WEB_INDEX_HTML = r"""<!DOCTYPE html>
|
|
|
2996
3045
|
<input type="text" id="addInput" placeholder="IPv4/IPv6, host, IPv4 CIDR, IPv6 /128, ip1-ip2">
|
|
2997
3046
|
<button id="btnAdd" title="add the given host(s) to the list - same input as DELETE">ADD</button>
|
|
2998
3047
|
<button id="btnDel" title="remove the given host(s) - same input as ADD">DELETE</button>
|
|
3048
|
+
<button id="btnList" title="paste a list of hosts (one per line, or separated by comma/semicolon/blank) to add or delete - also opened by Ctrl/Cmd+V on the page">ADD/DEL LIST</button>
|
|
2999
3049
|
</span>
|
|
3000
3050
|
<span class="sep"> | </span>
|
|
3001
3051
|
<span class="grp">
|
|
@@ -4085,11 +4135,85 @@ document.getElementById('commentInput').addEventListener('keydown', function(e){
|
|
|
4085
4135
|
document.getElementById('btnClear').onclick = function(){
|
|
4086
4136
|
if(confirm('Remove ALL hosts and reset their state?')){ post('clear'); } };
|
|
4087
4137
|
|
|
4138
|
+
/* ---- ADD/DEL LIST window (paste a host list), preview before applying ---- */
|
|
4139
|
+
var hostListModal = document.getElementById('hostListModal');
|
|
4140
|
+
var hostListText = document.getElementById('hostListText');
|
|
4141
|
+
var hostListInfo = document.getElementById('hostListInfo');
|
|
4142
|
+
var hostListAddBtn = document.getElementById('hostListAddBtn');
|
|
4143
|
+
var hostListDelBtn = document.getElementById('hostListDelBtn');
|
|
4144
|
+
var hostListTimer = null, hostListSeq = 0, hostListInList = 0, hostListNew = 0;
|
|
4145
|
+
function hostListOpen(){ return hostListModal.style.display === 'flex'; }
|
|
4146
|
+
function closeHostList(){ hostListModal.style.display = 'none'; hostListText.value = ''; }
|
|
4147
|
+
function openHostList(text){
|
|
4148
|
+
hostListText.value = text || '';
|
|
4149
|
+
hostListModal.style.display = 'flex';
|
|
4150
|
+
hostListPreviewNow();
|
|
4151
|
+
hostListText.focus();
|
|
4152
|
+
}
|
|
4153
|
+
function hostListPost(action){
|
|
4154
|
+
return fetch('api/hostlist', {method:'POST', headers:{'Content-Type':'application/json'},
|
|
4155
|
+
body: JSON.stringify({text: hostListText.value, action: action})}).then(function(r){ return r.json(); });
|
|
4156
|
+
}
|
|
4157
|
+
function hostListPreviewNow(){
|
|
4158
|
+
var seq = ++hostListSeq;
|
|
4159
|
+
if(!hostListText.value.trim()){
|
|
4160
|
+
hostListInfo.innerHTML = ' '; hostListAddBtn.disabled = true; hostListDelBtn.disabled = true; return;
|
|
4161
|
+
}
|
|
4162
|
+
hostListPost('preview').then(function(j){
|
|
4163
|
+
if(seq !== hostListSeq || !j.ok) return;
|
|
4164
|
+
hostListInList = j.in_list; hostListNew = j.new;
|
|
4165
|
+
var t = j.found + ' host(s) found: ' + j.new + ' new (ADD), ' + j.in_list + ' already in list (DELETE)';
|
|
4166
|
+
if(j.csv) t += ' - CSV: existing hosts are updated with its data' + (j.renamed ? ' (' + j.renamed + ' IP entries renamed to hostname)' : '');
|
|
4167
|
+
if(j.networks) t += ' - ' + j.networks + ' network(s) expanded';
|
|
4168
|
+
if(j.ignored) t += ' - ' + j.ignored + ' ignored: ' + j.ignored_sample.join(', ') + (j.ignored > j.ignored_sample.length ? ' ...' : '');
|
|
4169
|
+
if(j.skipped.length) t += ' - mask out of range: ' + j.skipped.join(', ');
|
|
4170
|
+
hostListInfo.textContent = t;
|
|
4171
|
+
hostListAddBtn.disabled = !(j.new || (j.csv && (j.in_list || j.renamed)));
|
|
4172
|
+
hostListDelBtn.disabled = !j.in_list;
|
|
4173
|
+
}).catch(function(){});
|
|
4174
|
+
}
|
|
4175
|
+
hostListText.addEventListener('input', function(){
|
|
4176
|
+
clearTimeout(hostListTimer); hostListTimer = setTimeout(hostListPreviewNow, 300); });
|
|
4177
|
+
function hostListRun(action){
|
|
4178
|
+
note(action === 'add' ? 'adding host list ...' : 'removing host list ...', true);
|
|
4179
|
+
hostListPost(action).then(function(j){ if(!j.ok){ note('list failed: ' + (j.error||''), false); } })
|
|
4180
|
+
.catch(function(){ note('list failed', false); });
|
|
4181
|
+
closeHostList();
|
|
4182
|
+
}
|
|
4183
|
+
hostListAddBtn.onclick = function(){ hostListRun('add'); };
|
|
4184
|
+
hostListDelBtn.onclick = function(){
|
|
4185
|
+
if(hostListInList > 20 && !confirm('Remove ' + hostListInList + ' hosts from the list?')) return;
|
|
4186
|
+
hostListRun('del'); };
|
|
4187
|
+
document.getElementById('hostListCancelBtn').onclick = closeHostList;
|
|
4188
|
+
document.getElementById('btnList').onclick = function(){ openHostList(''); };
|
|
4189
|
+
/* Ctrl/Cmd+V with no field focused and no window open: open the window with the clipboard text */
|
|
4190
|
+
document.addEventListener('paste', function(e){
|
|
4191
|
+
var a = document.activeElement, tn = a && a.tagName;
|
|
4192
|
+
if(isReadOnly || hostListOpen() || tn === 'INPUT' || tn === 'TEXTAREA' || tn === 'SELECT') return;
|
|
4193
|
+
if(document.querySelector('.modal-overlay[style*="flex"]')) return;
|
|
4194
|
+
var t = e.clipboardData && e.clipboardData.getData('text');
|
|
4195
|
+
if(!t || !t.trim()) return;
|
|
4196
|
+
e.preventDefault();
|
|
4197
|
+
openHostList(t);
|
|
4198
|
+
});
|
|
4199
|
+
|
|
4088
4200
|
/* ---- host file upload ---- */
|
|
4089
4201
|
var fileInput = document.getElementById('fileInput');
|
|
4202
|
+
var fileInputVis = document.getElementById('fileInputVis');
|
|
4203
|
+
var addFromClientModal = document.getElementById('addFromClientModal');
|
|
4204
|
+
function closeAddFromClient(){ addFromClientModal.style.display = 'none'; fileInputVis.value = ''; }
|
|
4205
|
+
function addFromClientOpen(){ return addFromClientModal.style.display === 'flex'; }
|
|
4206
|
+
document.getElementById('modalBtnAddFromClientCancel').onclick = closeAddFromClient;
|
|
4207
|
+
fileInputVis.addEventListener('change', function(){
|
|
4208
|
+
var f = fileInputVis.files && fileInputVis.files[0];
|
|
4209
|
+
closeAddFromClient();
|
|
4210
|
+
if(f) uploadHostsFile(f, null);
|
|
4211
|
+
});
|
|
4090
4212
|
fileInput.addEventListener('change', function(){
|
|
4091
4213
|
var f = fileInput.files && fileInput.files[0];
|
|
4092
|
-
if(
|
|
4214
|
+
if(f) uploadHostsFile(f, fileInput);
|
|
4215
|
+
});
|
|
4216
|
+
function uploadHostsFile(f, inp){
|
|
4093
4217
|
var rd = new FileReader();
|
|
4094
4218
|
rd.onload = function(){
|
|
4095
4219
|
note('uploading ' + f.name + ' ...', true);
|
|
@@ -4097,10 +4221,10 @@ fileInput.addEventListener('change', function(){
|
|
|
4097
4221
|
body: rd.result}).then(function(r){ return r.json(); }).then(function(j){
|
|
4098
4222
|
if(!j.ok){ note('upload failed: ' + (j.error||''), false); }
|
|
4099
4223
|
}).catch(function(){ note('upload failed', false); });
|
|
4100
|
-
|
|
4224
|
+
if(inp) inp.value = '';
|
|
4101
4225
|
};
|
|
4102
4226
|
rd.readAsText(f);
|
|
4103
|
-
}
|
|
4227
|
+
}
|
|
4104
4228
|
|
|
4105
4229
|
var uploadServerFileInput = document.getElementById('uploadServerFileInput');
|
|
4106
4230
|
uploadServerFileInput.addEventListener('change', function(){
|
|
@@ -4367,7 +4491,7 @@ document.getElementById('selDownload').onchange = function(){
|
|
|
4367
4491
|
this.value = ''; // reset - a select, not a toggle
|
|
4368
4492
|
if(!what) return;
|
|
4369
4493
|
if(what === 'add_from_server'){ openAddFromServer(); return; }
|
|
4370
|
-
if(what === 'add_from_client'){
|
|
4494
|
+
if(what === 'add_from_client'){ addFromClientModal.style.display = 'flex'; return; } // visible input: Safari blocks click() from select change
|
|
4371
4495
|
if(what === 'choose_logfile'){ openChooseLog(); return; }
|
|
4372
4496
|
if(what === 'delete_files'){ openDeleteFiles(); return; }
|
|
4373
4497
|
if(what === 'view_file'){ openViewFile(); return; }
|
|
@@ -4489,6 +4613,14 @@ document.addEventListener('keydown', function(e){
|
|
|
4489
4613
|
if(sk === 'y' || e.key === 'Enter'){ runSaveHostsFile(); e.preventDefault(); }
|
|
4490
4614
|
return;
|
|
4491
4615
|
}
|
|
4616
|
+
if(hostListOpen()){
|
|
4617
|
+
if(e.key === 'Escape'){ closeHostList(); e.preventDefault(); }
|
|
4618
|
+
return;
|
|
4619
|
+
}
|
|
4620
|
+
if(addFromClientOpen()){
|
|
4621
|
+
if(e.key === 'Escape'){ closeAddFromClient(); e.preventDefault(); }
|
|
4622
|
+
return;
|
|
4623
|
+
}
|
|
4492
4624
|
if(chooseLogOpen()){
|
|
4493
4625
|
if(e.key === 'Escape'){ closeChooseLog(); e.preventDefault(); }
|
|
4494
4626
|
return;
|
|
@@ -5776,6 +5908,45 @@ class EpingWebHandler(http.server.BaseHTTPRequestHandler):
|
|
|
5776
5908
|
self._respond(200, 'application/json; charset=utf-8', json.dumps({'ok': True}))
|
|
5777
5909
|
return
|
|
5778
5910
|
|
|
5911
|
+
# --- ADD/DEL LIST window: preview (no change) or queue add_list/del_list ---
|
|
5912
|
+
if path in ('/api/hostlist', 'api/hostlist'):
|
|
5913
|
+
raw = self._read_body(WEB_MAX_UPLOAD)
|
|
5914
|
+
if raw is None:
|
|
5915
|
+
self._respond(413, 'application/json; charset=utf-8',
|
|
5916
|
+
json.dumps({'ok': False, 'error': 'text too large'}))
|
|
5917
|
+
return
|
|
5918
|
+
try:
|
|
5919
|
+
req = json.loads(raw.decode('utf-8', 'replace') or '{}')
|
|
5920
|
+
except Exception:
|
|
5921
|
+
req = {}
|
|
5922
|
+
text = str(req.get('text', ''))
|
|
5923
|
+
action = str(req.get('action', 'preview'))
|
|
5924
|
+
if action in ('add', 'del'):
|
|
5925
|
+
if not self._queue_command((action + '_list', text)):
|
|
5926
|
+
return
|
|
5927
|
+
self._respond(200, 'application/json; charset=utf-8', json.dumps({'ok': True}))
|
|
5928
|
+
return
|
|
5929
|
+
pstats = {}
|
|
5930
|
+
_m0, _c0 = set(host_meta), set(host_csv_ip)
|
|
5931
|
+
found = parse_hosts_from_text(text, pstats, True)
|
|
5932
|
+
with web_lock:
|
|
5933
|
+
present = set(web_state.get('host_list_all') or [])
|
|
5934
|
+
n_ren = (sum(1 for h in found if h not in present and host_csv_ip.get(h) in present
|
|
5935
|
+
and host_csv_ip.get(h) != h) if pstats.get('csv') else 0)
|
|
5936
|
+
for _k in set(host_meta) - _m0: # preview only - keep no CSV data of it
|
|
5937
|
+
host_meta.pop(_k, None)
|
|
5938
|
+
for _k in set(host_csv_ip) - _c0:
|
|
5939
|
+
host_csv_ip.pop(_k, None)
|
|
5940
|
+
n_in = sum(1 for h in found if h in present)
|
|
5941
|
+
self._respond(200, 'application/json; charset=utf-8', json.dumps({
|
|
5942
|
+
'ok': True, 'found': len(found), 'in_list': n_in,
|
|
5943
|
+
'csv': bool(pstats.get('csv')), 'renamed': n_ren,
|
|
5944
|
+
'new': len(found) - n_in - n_ren, 'networks': pstats.get('networks', 0),
|
|
5945
|
+
'ignored': len(pstats.get('ignored', [])),
|
|
5946
|
+
'ignored_sample': pstats.get('ignored', [])[:8],
|
|
5947
|
+
'skipped': pstats.get('skipped', [])[:5]}))
|
|
5948
|
+
return
|
|
5949
|
+
|
|
5779
5950
|
# --- FILE ACTIONS > UPLOAD: save a *.txt/*.csv file as-is, no host
|
|
5780
5951
|
# parsing (see /api/upload above for that) ---
|
|
5781
5952
|
if path in ('/api/upload_server_file', 'api/upload_server_file'):
|
|
@@ -6632,6 +6803,47 @@ def run_web_mode(original_hosts_list, host_state, args, logfile_file_name,
|
|
|
6632
6803
|
message += ' - please wait, pinging ...'
|
|
6633
6804
|
write_log_info(args.disable_logging, logfile_file_name,
|
|
6634
6805
|
'ADD -f (uploaded, ' + str(added) + ' host(s))', tz_offset)
|
|
6806
|
+
elif cmd in ('add_list', 'del_list'):
|
|
6807
|
+
lst_stats = {}
|
|
6808
|
+
lst_hosts = parse_hosts_from_text(value, lst_stats, addr_mode not in (2, 3))
|
|
6809
|
+
if not lst_hosts:
|
|
6810
|
+
message = ('list: mask must be /%d../%d: %s'
|
|
6811
|
+
% (CIDR_MIN_MASK, CIDR_MAX_MASK, lst_stats['skipped'][0])
|
|
6812
|
+
if lst_stats.get('skipped') else 'list: no valid host found')
|
|
6813
|
+
elif cmd == 'add_list':
|
|
6814
|
+
renamed = (merge_csv_ips_into_lists(lst_hosts, original_hosts_list,
|
|
6815
|
+
active_hosts_list, host_state,
|
|
6816
|
+
up_seen, down_streak)
|
|
6817
|
+
if lst_stats.get('csv') else 0)
|
|
6818
|
+
before_active = set(active_hosts_list)
|
|
6819
|
+
added, err = add_hosts_to(lst_hosts, active_hosts_list, original_hosts_list)
|
|
6820
|
+
if err:
|
|
6821
|
+
message = 'list ' + err
|
|
6822
|
+
else:
|
|
6823
|
+
message = ('list: ' + str(added) + ' new host(s) of '
|
|
6824
|
+
+ str(len(lst_hosts)) + ' found')
|
|
6825
|
+
if lst_stats.get('csv'):
|
|
6826
|
+
message += (', %d existing host(s) updated (%d renamed to hostname)'
|
|
6827
|
+
% (len(lst_hosts) - added, renamed))
|
|
6828
|
+
if lst_stats.get('ignored'):
|
|
6829
|
+
message += ', %d ignored' % len(lst_stats['ignored'])
|
|
6830
|
+
if added:
|
|
6831
|
+
pending_add_hosts |= (set(lst_hosts) - before_active)
|
|
6832
|
+
message += ' - please wait, pinging ...'
|
|
6833
|
+
write_log_info(args.disable_logging, logfile_file_name,
|
|
6834
|
+
'ADD list (' + str(added) + ' host(s))', tz_offset)
|
|
6835
|
+
else:
|
|
6836
|
+
removed = remove_hosts_from(lst_hosts, active_hosts_list,
|
|
6837
|
+
original_hosts_list, host_state)
|
|
6838
|
+
up_seen.difference_update(lst_hosts)
|
|
6839
|
+
for _h in lst_hosts:
|
|
6840
|
+
down_streak.pop(_h, None)
|
|
6841
|
+
forget_names(lst_hosts)
|
|
6842
|
+
message = ('list: removed ' + str(removed) + ' of '
|
|
6843
|
+
+ str(len(lst_hosts)) + ' host(s) found')
|
|
6844
|
+
if removed:
|
|
6845
|
+
write_log_info(args.disable_logging, logfile_file_name,
|
|
6846
|
+
'DEL list (' + str(removed) + ' host(s))', tz_offset)
|
|
6635
6847
|
elif cmd == 'set_ref':
|
|
6636
6848
|
# what is actually shown becomes the new reference list - the view
|
|
6637
6849
|
# filter (active_hosts_list) AND the [M]/display filter (display_list
|
|
@@ -8331,6 +8543,52 @@ if __name__=='__main__':
|
|
|
8331
8543
|
write_log_info(args.disable_logging, logfile_file_name,
|
|
8332
8544
|
host_spec_cli('DEL', bval), tz_offset)
|
|
8333
8545
|
|
|
8546
|
+
elif bcmd in ('add_list', 'del_list'):
|
|
8547
|
+
lst_stats = {}
|
|
8548
|
+
lst_hosts = parse_hosts_from_text(bval, lst_stats, not (prefer_ip or ip_only_mode))
|
|
8549
|
+
if not lst_hosts:
|
|
8550
|
+
message = ('list: mask must be /%d..%d: %s'
|
|
8551
|
+
% (CIDR_MIN_MASK, CIDR_MAX_MASK, lst_stats['skipped'][0])
|
|
8552
|
+
if lst_stats.get('skipped') else 'list: no valid host found')
|
|
8553
|
+
notice(message.upper(), 3)
|
|
8554
|
+
elif bcmd == 'add_list':
|
|
8555
|
+
renamed = (merge_csv_ips_into_lists(lst_hosts, original_hosts_list,
|
|
8556
|
+
active_hosts_list, host_state,
|
|
8557
|
+
up_seen, down_streak)
|
|
8558
|
+
if lst_stats.get('csv') else 0)
|
|
8559
|
+
before_active = set(active_hosts_list)
|
|
8560
|
+
added, err = add_hosts_to(lst_hosts, active_hosts_list, original_hosts_list)
|
|
8561
|
+
if err:
|
|
8562
|
+
notice(('LIST ' + err).upper(), 3)
|
|
8563
|
+
message = 'list ' + err
|
|
8564
|
+
else:
|
|
8565
|
+
message = ('list: ' + str(added) + ' new host(s) of '
|
|
8566
|
+
+ str(len(lst_hosts)) + ' found')
|
|
8567
|
+
if lst_stats.get('csv'):
|
|
8568
|
+
message += (', %d existing host(s) updated (%d renamed to hostname)'
|
|
8569
|
+
% (len(lst_hosts) - added, renamed))
|
|
8570
|
+
if lst_stats.get('ignored'):
|
|
8571
|
+
message += ', %d ignored' % len(lst_stats['ignored'])
|
|
8572
|
+
notice(message.upper(), 2)
|
|
8573
|
+
if added:
|
|
8574
|
+
pending_add_hosts |= (set(lst_hosts) - before_active)
|
|
8575
|
+
message += ' - please wait, pinging ...'
|
|
8576
|
+
write_log_info(args.disable_logging, logfile_file_name,
|
|
8577
|
+
'ADD list (' + str(added) + ' host(s))', tz_offset)
|
|
8578
|
+
else:
|
|
8579
|
+
removed = remove_hosts_from(lst_hosts, active_hosts_list,
|
|
8580
|
+
original_hosts_list, host_state)
|
|
8581
|
+
up_seen.difference_update(lst_hosts)
|
|
8582
|
+
for _h in lst_hosts:
|
|
8583
|
+
down_streak.pop(_h, None)
|
|
8584
|
+
forget_names(lst_hosts)
|
|
8585
|
+
message = ('list: removed ' + str(removed) + ' of '
|
|
8586
|
+
+ str(len(lst_hosts)) + ' host(s) found')
|
|
8587
|
+
notice(message.upper(), 2 if removed else 3)
|
|
8588
|
+
if removed:
|
|
8589
|
+
write_log_info(args.disable_logging, logfile_file_name,
|
|
8590
|
+
'DEL list (' + str(removed) + ' host(s))', tz_offset)
|
|
8591
|
+
|
|
8334
8592
|
elif bcmd == 'upload':
|
|
8335
8593
|
up_stats = {}
|
|
8336
8594
|
new_hosts = parse_hosts_from_text(bval, up_stats, not (prefer_ip or ip_only_mode))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: eping
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.91
|
|
4
4
|
Summary: Continuous ICMP reachability monitor built on fping, with a CSV logfile analyser (epinga)
|
|
5
5
|
Author-email: Ewald Jeitler <ewald@jeitler.cc>
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -17,7 +17,7 @@ Description-Content-Type: text/markdown
|
|
|
17
17
|
License-File: LICENSE
|
|
18
18
|
Dynamic: license-file
|
|
19
19
|
|
|
20
|
-
# eping.py 3.
|
|
20
|
+
# eping.py 3.91
|
|
21
21
|
|
|
22
22
|
Continuous ICMP reachability monitor built on `fping`. Scans a host list in a loop,
|
|
23
23
|
reports each host UP/DOWN/NO-DNS, counts state changes. CLI (curses) or web GUI.
|
|
@@ -244,7 +244,7 @@ Single self-contained page, no external resources.
|
|
|
244
244
|
- Row 1: view select (9 views), sort order select, SET REFERENCE, ZERO CHANGES,
|
|
245
245
|
CLEAR ALL, address mode select, GET NAMES, ADV OPTIONS, RESET LOG, FILE OPERATIONS
|
|
246
246
|
select, EXIT, font size.
|
|
247
|
-
- Row 2: match filter (SET/CLEAR), ADV FILTER, host field (ADD/DELETE), comment
|
|
247
|
+
- Row 2: match filter (SET/CLEAR), ADV FILTER, host field (ADD/DELETE), ADD/DEL LIST, comment
|
|
248
248
|
field (COMMENT), GENERATE REPORT select.
|
|
249
249
|
- View select, sort order select, address mode select and ADV OPTIONS all match the
|
|
250
250
|
CLI's `U`/`O`/`P`/`X` pickers exactly. FILE OPERATIONS (incl. ADD HOSTS FILE FROM CLIENT/
|
|
@@ -254,7 +254,7 @@ Single self-contained page, no external resources.
|
|
|
254
254
|
| Entry | Does |
|
|
255
255
|
|---|---|
|
|
256
256
|
| ADD HOSTS FILE FROM SERVER | picks one or more `.txt` files or inventory `.csv` files already on the server, adds their hosts; a `.csv` without the inventory header, e.g. an eping log, is refused |
|
|
257
|
-
| ADD HOSTS FILE FROM CLIENT |
|
|
257
|
+
| ADD HOSTS FILE FROM CLIENT | dialog with a file picker (`.txt`/`.csv`; works in Safari), adds the hosts; a file can also be dropped anywhere on the page |
|
|
258
258
|
| SAVE HOSTS FILE ON SERVER | confirmation, then saves the currently displayed hosts; blank name overwrites the active `-f` hostfile (first one, if eping.py was started with one) or, with none active, the default `eping-hosts.txt`; a typed name always saves as that new file instead, `.txt` appended once, active hostfile left untouched. If the displayed hosts carry inventory values (CSV import) an inventory `.csv` is written instead (`;` delimited, columns as in *Inventory CSV import*); a `.txt` name or hostfile then becomes `.csv`, the default is `eping-hosts.csv`. The options that differ from the default are written first as `opt:` lines (see *Host file format*); for a `.csv` the checkboxes `include options (opt: lines)` (default on) and `save as .txt (hosts only, no inventory)` (default off) are offered |
|
|
259
259
|
| UPLOAD FILE TO SERVER | picks a local `.txt`/`.csv`, saves as-is server-side, no host parsing; never overwrites, appends `-1`/`-2`/... |
|
|
260
260
|
| DOWNLOAD ALL HOSTS | full reference list: inventory `.csv` if the hosts carry inventory values, else `.txt`; non-default options first as `opt:` lines (`.csv`: checkboxes `include options`, default on, and `save as .txt`, default off) |
|
|
@@ -273,6 +273,13 @@ Single self-contained page, no external resources.
|
|
|
273
273
|
`+`/`−` font size (works while typing). No keyboard shortcut for `X`/ADV OPTIONS or
|
|
274
274
|
GENERATE REPORT - click required.
|
|
275
275
|
- Host field feeds both ADD/DELETE; ENTER triggers the button used last, ESC clears.
|
|
276
|
+
- **ADD/DEL LIST** (button after DELETE; also Ctrl/Cmd+V on the page while no field has focus): paste a
|
|
277
|
+
host list - one per line or separated by comma/semicolon/blank, `#` comment, CIDR expanded. A live
|
|
278
|
+
preview shows found / new / already in list / ignored words / masks out of range; ADD and DELETE are
|
|
279
|
+
enabled only if they would change something (DELETE over 20 hosts asks to confirm; exact name match
|
|
280
|
+
only). A pasted inventory `.csv` (with the `IP Address`/`Hostname` header) also updates hosts already in
|
|
281
|
+
the list: IP entries (e.g. from a `.txt`) are renamed to the CSV hostname, history kept, and the CSV
|
|
282
|
+
values are attached. Works with `-wvc` too.
|
|
276
283
|
- Files can also be dropped anywhere on the page (max 16MB, see *Host file format*).
|
|
277
284
|
- Commands acknowledge immediately; a click aborts the running round.
|
|
278
285
|
- **Host popup**: hovering a host row (after ~350 ms) shows everything known about it:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|