diff --git a/scripts/updates-uptime-cmd.py b/scripts/updates-uptime-cmd.py index d5fbe2ec34..005e928158 100755 --- a/scripts/updates-uptime-cmd.py +++ b/scripts/updates-uptime-cmd.py @@ -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' : '..org', + '.fedorainfracloud.org' : '..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()