forked from infra/ansible
updates-uptimes: Stats command and UI improvements.
Signed-off-by: James Antill <james@and.org>
This commit is contained in:
parent
68d7e76db9
commit
38f3be9ca9
1 changed files with 157 additions and 54 deletions
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue