eping 3.86__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.86 → eping-3.91}/PKG-INFO +10 -3
- {eping-3.86 → eping-3.91}/README.md +9 -2
- {eping-3.86 → eping-3.91}/pyproject.toml +1 -1
- {eping-3.86 → eping-3.91}/src/eping/eping.py +234 -2
- {eping-3.86 → eping-3.91}/src/eping.egg-info/PKG-INFO +10 -3
- {eping-3.86 → eping-3.91}/LICENSE +0 -0
- {eping-3.86 → eping-3.91}/setup.cfg +0 -0
- {eping-3.86 → eping-3.91}/src/eping/__init__.py +0 -0
- {eping-3.86 → eping-3.91}/src/eping/_launch_eping.py +0 -0
- {eping-3.86 → eping-3.91}/src/eping/epinga.py +0 -0
- {eping-3.86 → eping-3.91}/src/eping.egg-info/SOURCES.txt +0 -0
- {eping-3.86 → eping-3.91}/src/eping.egg-info/dependency_links.txt +0 -0
- {eping-3.86 → eping-3.91}/src/eping.egg-info/entry_points.txt +0 -0
- {eping-3.86 → 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/
|
|
@@ -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/
|
|
@@ -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,19 @@ 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>
|
|
2851
2890
|
<div id="addFromClientModal" class="modal-overlay" style="display:none">
|
|
2852
2891
|
<div class="modal-box">
|
|
2853
2892
|
<h3>ADD FROM CLIENT</h3>
|
|
@@ -3006,6 +3045,7 @@ WEB_INDEX_HTML = r"""<!DOCTYPE html>
|
|
|
3006
3045
|
<input type="text" id="addInput" placeholder="IPv4/IPv6, host, IPv4 CIDR, IPv6 /128, ip1-ip2">
|
|
3007
3046
|
<button id="btnAdd" title="add the given host(s) to the list - same input as DELETE">ADD</button>
|
|
3008
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>
|
|
3009
3049
|
</span>
|
|
3010
3050
|
<span class="sep"> | </span>
|
|
3011
3051
|
<span class="grp">
|
|
@@ -4095,6 +4135,68 @@ document.getElementById('commentInput').addEventListener('keydown', function(e){
|
|
|
4095
4135
|
document.getElementById('btnClear').onclick = function(){
|
|
4096
4136
|
if(confirm('Remove ALL hosts and reset their state?')){ post('clear'); } };
|
|
4097
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
|
+
|
|
4098
4200
|
/* ---- host file upload ---- */
|
|
4099
4201
|
var fileInput = document.getElementById('fileInput');
|
|
4100
4202
|
var fileInputVis = document.getElementById('fileInputVis');
|
|
@@ -4511,6 +4613,10 @@ document.addEventListener('keydown', function(e){
|
|
|
4511
4613
|
if(sk === 'y' || e.key === 'Enter'){ runSaveHostsFile(); e.preventDefault(); }
|
|
4512
4614
|
return;
|
|
4513
4615
|
}
|
|
4616
|
+
if(hostListOpen()){
|
|
4617
|
+
if(e.key === 'Escape'){ closeHostList(); e.preventDefault(); }
|
|
4618
|
+
return;
|
|
4619
|
+
}
|
|
4514
4620
|
if(addFromClientOpen()){
|
|
4515
4621
|
if(e.key === 'Escape'){ closeAddFromClient(); e.preventDefault(); }
|
|
4516
4622
|
return;
|
|
@@ -5802,6 +5908,45 @@ class EpingWebHandler(http.server.BaseHTTPRequestHandler):
|
|
|
5802
5908
|
self._respond(200, 'application/json; charset=utf-8', json.dumps({'ok': True}))
|
|
5803
5909
|
return
|
|
5804
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
|
+
|
|
5805
5950
|
# --- FILE ACTIONS > UPLOAD: save a *.txt/*.csv file as-is, no host
|
|
5806
5951
|
# parsing (see /api/upload above for that) ---
|
|
5807
5952
|
if path in ('/api/upload_server_file', 'api/upload_server_file'):
|
|
@@ -6658,6 +6803,47 @@ def run_web_mode(original_hosts_list, host_state, args, logfile_file_name,
|
|
|
6658
6803
|
message += ' - please wait, pinging ...'
|
|
6659
6804
|
write_log_info(args.disable_logging, logfile_file_name,
|
|
6660
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)
|
|
6661
6847
|
elif cmd == 'set_ref':
|
|
6662
6848
|
# what is actually shown becomes the new reference list - the view
|
|
6663
6849
|
# filter (active_hosts_list) AND the [M]/display filter (display_list
|
|
@@ -8357,6 +8543,52 @@ if __name__=='__main__':
|
|
|
8357
8543
|
write_log_info(args.disable_logging, logfile_file_name,
|
|
8358
8544
|
host_spec_cli('DEL', bval), tz_offset)
|
|
8359
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
|
+
|
|
8360
8592
|
elif bcmd == 'upload':
|
|
8361
8593
|
up_stats = {}
|
|
8362
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/
|
|
@@ -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
|