1
0
Fork 0
forked from infra/ansible

updates-uptimes: Stats command and UI improvements.

Signed-off-by: James Antill <james@and.org>
This commit is contained in:
James Antill 2025-08-07 15:41:35 -04:00
commit 38f3be9ca9

View file

@ -36,6 +36,18 @@ import time
# ansible FACT cache.
conf_dur_flush_cache = (60*60*8)
# How many hosts to show in tier 4 updates/uptimes...
conf_stat_4_hosts = 4
# Remove suffix noise in names.
conf_suffix_dns_replace = {
'.fedoraproject.org' : '.<FP>.org',
'.fedorainfracloud.org' : '.<FIC>.org',
}
_suffix_dns_replace = {}
for x in conf_suffix_dns_replace:
_suffix_dns_replace[x] = False
# Dir. where we put, and look for, the files...
conf_path = "/var/log/"
@ -44,6 +56,11 @@ fname_today = fname + '.' + time.strftime("%Y-%m-%d")
backups = sorted(x.removeprefix(fname + '.') for x in glob.glob(fname + '.*'))
if len(sys.argv) >= 2:
if '-v' in sys.argv:
sys.argv.remove('-v') # In theory sys.argv[0] but meh
conf_suffix_dns_replace = {}
cmd = "diff"
if len(sys.argv) >= 2:
if sys.argv[1] in ("backups", "backups-keep", "diff", "diff-u", "help", "host", "info", "list", "stats", "update", "update-fast", "update-flush", "update-daily", "uptime",):
@ -124,18 +141,21 @@ def _cmp_arg():
def line2data(line):
name, rpms, uptime, date = line.split(' ', 3)
osname = "Unknown"
osvers = "0"
osvers = "?"
osinfo = "Unknown/?"
osinfo_small = "?/?"
if ' ' in date:
date, osname, osvers = date.split(' ', 2)
osinfo = osname + '/' + osvers
osinfo_small = osinfo
if osname == 'CentOS':
osinfo = 'EL/' + osvers
osinfo_small = 'EL/' + osvers
if osname == 'RedHat':
osinfo = 'EL/' + osvers
osinfo_small = 'EL/' + osvers
if osname == 'Fedora':
osinfo = 'F/' + osvers
osinfo_small = 'F/' + osvers
# This is the primary key for diff.
updates = name + ' ' + rpms
rpms = int(rpms)
@ -143,6 +163,25 @@ def line2data(line):
return locals()
# Sub. suffix of DNS names for UI
def _ui_name(name):
for suffix in conf_suffix_dns_replace:
if name.endswith(suffix):
_suffix_dns_replace[suffix] = True
return name[:-len(suffix)] + conf_suffix_dns_replace[suffix]
return name
# Reset the usage after _max_update()
def _reset_ui_name():
for suffix in sorted(_suffix_dns_replace):
_suffix_dns_replace[suffix] = False
# Explain if we used any suffix subs.
def _explain_ui_name():
for suffix in sorted(_suffix_dns_replace):
if _suffix_dns_replace[suffix]:
print("* NOTE: %12s = %s" %(conf_suffix_dns_replace[suffix],suffix))
def fname2lines(fname):
return [x.strip() for x in open(fname).readlines()]
@ -154,6 +193,37 @@ def fname1():
return bfname2lines(cmp)
return fname2lines(fname)
_max_len_name = 0
_max_len_rpms = 0
_max_len_updur = 0
_max_len_date = 0 # 4+1+2+1+2
_max_terminal_width = os.get_terminal_size().columns
if _max_terminal_width < 20:
_max_terminal_width = 80
_max_terminal_width -= 10
def _max_update(prefix, lines):
global _max_len_name
global _max_len_rpms
global _max_len_updur
global _max_len_date
for line in lines:
data = line2data(line)
name = _ui_name(data['name'])
if len(name) > _max_len_name:
_max_len_name = len(name)
if len(str(data['rpms'])) > _max_len_rpms:
_max_len_rpms = len(str(data['rpms']))
updur_len = len(format_duration(data['uptime'], static=True))
if updur_len > _max_len_updur:
_max_len_updur = updur_len
if len(data['date']) > _max_len_date:
_max_len_date = len(data['date'])
mw = _max_terminal_width - len(prefix)
while _max_len_name + _max_len_rpms + _max_len_updur + _max_len_date >= mw:
_max_len_name -= 1
def _usage():
prog = "updates+uptime"
if sys.argv:
@ -242,34 +312,82 @@ if cmd == "update-daily": # Also create backup file.
if cmd == "stats":
_cmp_arg()
data = fname1()
osinfo = {}
print("Hosts:", len(data))
most = 0, None
updates = 0
awake = 0
awakest = 0, None
# Basically we have hosts/updates/uptime and we want 4 tiers of data:
# 1. All. 2. OS name (Eg. Fedora). 3. OS name+version (Eg. Fedora 42).
# 4. For updates/uptime a "few" hosts with the biggest numbers.
osinfo = {'hosts' : {}, 'updates' : {}, 'uptimes' : {}}
updates = 0 # total updates
most = [] # Tier 4, for updates
awake = 0 # total uptime
awakest = [] # Tier 4, for uptime
_max_update('', data)
_reset_ui_name()
for line in data:
d2 = line2data(line)
if d2['osname'] not in osinfo:
osinfo[d2['osname']] = 0
osinfo[d2['osname']] += 1
if d2['osinfo'] not in osinfo:
osinfo[d2['osinfo']] = 0
osinfo[d2['osinfo']] += 1
# Tier 2/3 hosts...
if d2['osname'] not in osinfo['hosts']:
osinfo['hosts'][d2['osname']] = 0
osinfo['hosts'][d2['osname']] += 1
if d2['osinfo'] not in osinfo['hosts']:
osinfo['hosts'][d2['osinfo']] = 0
osinfo['hosts'][d2['osinfo']] += 1
updates += d2['rpms']
if d2['rpms'] >= most[0]:
most = d2['rpms'], d2['name']
# Tier 2/3 updates...
if d2['osname'] not in osinfo['updates']:
osinfo['updates'][d2['osname']] = 0
osinfo['updates'][d2['osname']] += d2['rpms']
if d2['osinfo'] not in osinfo['updates']:
osinfo['updates'][d2['osinfo']] = 0
osinfo['updates'][d2['osinfo']] += d2['rpms']
# Tier 4 updates...
most.append((d2['rpms'], d2['name']))
most.sort()
while len(most) > conf_stat_4_hosts:
most.pop(0)
awake += d2['uptime']
if d2['uptime'] >= awakest[0]:
awakest = d2['uptime'], d2['name']
for osi in sorted(osinfo):
print(" Hosts:", osi, osinfo[osi])
# Tier 2/3 uptimes...
if d2['osname'] not in osinfo['uptimes']:
osinfo['uptimes'][d2['osname']] = 0
osinfo['uptimes'][d2['osname']] += d2['uptime']
if d2['osinfo'] not in osinfo['uptimes']:
osinfo['uptimes'][d2['osinfo']] = 0
osinfo['uptimes'][d2['osinfo']] += d2['uptime']
# Tier 4 uptimes...
awakest.append((d2['uptime'], d2['name']))
awakest.sort()
while len(awakest) > conf_stat_4_hosts:
awakest.pop(0)
print("Hosts:", len(data))
for osi in sorted(osinfo['hosts']):
if '/' not in osi:
print(" %s: %s" % (osi, osinfo['hosts'][osi]))
if '/' in osi:
print(" %s: %s" % (osi, osinfo['hosts'][osi]))
print("Updates:", updates)
print(" Most:", most[1], most[0])
for osi in sorted(osinfo['updates']):
if '/' not in osi:
print(" %s: %s" % (osi, osinfo['updates'][osi]))
if '/' in osi:
print(" %s: %s" % (osi, osinfo['updates'][osi]))
for m in most:
print(" Most: %-*s %s" % (_max_len_name, _ui_name(m[1]), m[0]))
print("Uptime:", format_duration(awake))
print(" Most:", awakest[1], format_duration(awakest[0]))
for osi in sorted(osinfo['uptimes']):
if '/' not in osi:
print(" %s: %s" % (osi, format_duration(osinfo['uptimes'][osi], True)))
if '/' in osi:
print(" %s: %s" % (osi,format_duration(osinfo['uptimes'][osi],True)))
for a in awakest:
print(" Most: %-*s %s" % (_max_len_name, _ui_name(a[1]),
format_duration(a[0], True)))
_explain_ui_name()
def _print_info(host, lines):
hosts = []
@ -282,7 +400,7 @@ def _print_info(host, lines):
sys.exit(2)
for host in hosts:
print("Host:", host['name'])
print(" OS:", host['osinfo'], host['osname'], host['osvers'])
print(" OS:", host['osinfo'])
print(" Rpms:", host['rpms'])
print(" Uptime:", format_duration(host['uptime']))
print(" Date:", host['date'])
@ -297,40 +415,19 @@ if cmd in ("host", "info"):
if len(sys.argv) >= 2 and sys.argv[1] == "all":
for b in backups:
print("Backup:", b)
_print_info(bfname2lines(b))
_print_info(host, bfname2lines(b))
sys.argv = [sys.argv[0]]
print("Main:")
_cmp_arg()
_print_info(host, fname1())
_max_len_name = 0
_max_len_rpms = 0
_max_len_updur = 0
_max_len_date = 0 # 4+1+2+1+2
def _max_update(lines):
global _max_len_name
global _max_len_rpms
global _max_len_updur
global _max_len_date
for line in lines:
data = line2data(line)
if len(data['name']) > _max_len_name:
_max_len_name = len(data['name'])
if len(str(data['rpms'])) > _max_len_rpms:
_max_len_rpms = len(str(data['rpms']))
updur_len = len(format_duration(data['uptime'], static=True))
if updur_len > _max_len_updur:
_max_len_updur = updur_len
if len(data['date']) > _max_len_date:
_max_len_date = len(data['date'])
def _print_line(prefix, data):
print("%s%-*s %*u %*s %*s %s" % (prefix,
_max_len_name, data['name'],
_max_len_name, _ui_name(data['name']),
_max_len_rpms, data['rpms'],
_max_len_updur, format_duration(data['uptime'], static=True),
_max_len_date, data['date'], data['osinfo']))
_max_len_date, data['date'], data['osinfo_small']))
if cmd == "list":
host = "*"
@ -339,12 +436,14 @@ if cmd == "list":
_cmp_arg()
lines = fname1()
_max_update(lines)
_max_update('', lines)
_reset_ui_name()
for line in lines:
d1 = line2data(line)
if not fnmatch.fnmatch(d1['name'], host):
continue
_print_line('', d1)
_explain_ui_name()
if cmd == "uptime":
age = 0
@ -353,12 +452,14 @@ if cmd == "uptime":
_cmp_arg()
lines = fname1()
_max_update(lines)
_max_update('', lines)
_reset_ui_name()
for line in lines:
d1 = line2data(line)
if age > d1['uptime']:
continue
_print_line('', d1)
_explain_ui_name()
if cmd in ("diff", "diff-u"):
_cmp_arg()
@ -370,8 +471,9 @@ if cmd in ("diff", "diff-u"):
fn2 = fname + '.' + sys.argv[2]
data2 = fname2lines(fn2)
print("diff %s %s" % (fn1, fn2), file=sys.stderr)
_max_update(data1)
_max_update(data2)
_max_update(' ', data1)
_max_update(' ', data2)
_reset_ui_name()
while len(data1) > 0 or len(data2) > 0:
if len(data1) <= 0:
_print_line('+', line2data(data2[0]))
@ -414,4 +516,5 @@ if cmd in ("diff", "diff-u"):
data2.pop(0)
continue
_explain_ui_name()