223 lines
7.3 KiB
Python
223 lines
7.3 KiB
Python
#! /usr/bin/env python3
|
|
# -*- coding: utf-8; py-indent-offset: 4 -*-
|
|
#
|
|
# Author: Linuxfabrik GmbH, Zurich, Switzerland
|
|
# Contact: info (at) linuxfabrik (dot) ch
|
|
# https://www.linuxfabrik.ch/
|
|
# License: The Unlicense, see LICENSE file.
|
|
|
|
# https://github.com/Linuxfabrik/monitoring-plugins/blob/main/CONTRIBUTING.rst
|
|
|
|
"""Functions to convert raw numbers, times etc. to a human readable representation (and sometimes
|
|
back).
|
|
"""
|
|
|
|
__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
|
|
__version__ = '2023051201'
|
|
|
|
import math
|
|
|
|
|
|
def bits2human(n, _format='%(value).1f%(symbol)s'):
|
|
"""Converts n bits to a human readable format.
|
|
|
|
>>> bits2human(8191)
|
|
'1023.9B'
|
|
>>> bits2human(8192)
|
|
'1.0KiB'
|
|
"""
|
|
symbols = ('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB')
|
|
prefix = {}
|
|
prefix['B'] = 8
|
|
for i, s in enumerate(symbols[1:]):
|
|
prefix[s] = 1024 ** (i + 1) * 8
|
|
for symbol in reversed(symbols):
|
|
if n >= prefix[symbol]:
|
|
value = float(n) / prefix[symbol] # pylint: disable=W0641
|
|
return _format % locals()
|
|
return _format % dict(symbol=symbols[0], value=n)
|
|
|
|
|
|
def bps2human(n, _format='%(value).1f%(symbol)s'):
|
|
"""Converts n bits per scond to a human readable format.
|
|
|
|
>>> bps2human(72000000)
|
|
'72Mbps'
|
|
"""
|
|
symbols = ('bps', 'Kbps', 'Mbps', 'Gbps', 'Tbps', 'Pbps', 'Ebps', 'Zbps', 'Ybps')
|
|
prefix = {}
|
|
for i, s in enumerate(symbols[1:]):
|
|
prefix[s] = 1000 ** (i + 1)
|
|
for symbol in reversed(symbols[1:]):
|
|
if n >= prefix[symbol]:
|
|
value = float(n) / prefix[symbol] # pylint: disable=W0641
|
|
return _format % locals()
|
|
return _format % dict(symbol=symbols[0], value=n)
|
|
|
|
|
|
def bytes2human(n, _format='%(value).1f%(symbol)s'):
|
|
"""Converts n bytes to a human readable format.
|
|
|
|
>>> bytes2human(1023)
|
|
'1023.0B'
|
|
>>> bytes2human(1024)
|
|
'1.0KiB'
|
|
|
|
https://github.com/giampaolo/psutil/blob/master/psutil/_common.py
|
|
"""
|
|
symbols = ('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB')
|
|
prefix = {}
|
|
for i, s in enumerate(symbols[1:]):
|
|
# Returns 1 with the bits shifted to the left by (i + 1)*10 places
|
|
# (and new bits on the right-hand-side are zeros). This is the same
|
|
# as multiplying x by 2**y.
|
|
prefix[s] = 1 << (i + 1) * 10
|
|
for symbol in reversed(symbols[1:]):
|
|
if n >= prefix[symbol]:
|
|
value = float(n) / prefix[symbol] # pylint: disable=W0641
|
|
return _format % locals()
|
|
return _format % dict(symbol=symbols[0], value=n)
|
|
|
|
|
|
def human2bytes(string, binary=True):
|
|
"""Converts a string such as '3.072GiB' to 3298534883 bytes. If "binary" is set to True
|
|
(default due to Microsoft), it will use powers of 1024, otherwise powers of 1000 (decimal).
|
|
Returns 0 on failure.
|
|
|
|
Works with:
|
|
* 3.072GiB (always multiplied by 1024)
|
|
* 3.072GB (multiplied by 1024 if binary == True, else multiplied by 1000)
|
|
* 3.072G (multiplied by 1024 if binary == True, else multiplied by 1000)
|
|
"""
|
|
try:
|
|
string = string.lower()
|
|
if 'kib' in string:
|
|
return int(float(string.replace('kib', '').strip()) * 1024)
|
|
if 'mib' in string:
|
|
return int(float(string.replace('mib', '').strip()) * 1024 ** 2)
|
|
if 'gib' in string:
|
|
return int(float(string.replace('gib', '').strip()) * 1024 ** 3)
|
|
if 'tib' in string:
|
|
return int(float(string.replace('tib', '').strip()) * 1024 ** 4)
|
|
if 'pib' in string:
|
|
return int(float(string.replace('pib', '').strip()) * 1024 ** 5)
|
|
|
|
if 'k' in string: # matches "kb" or "k"
|
|
string = string.replace('kb', '').replace('k', '').strip()
|
|
if binary:
|
|
return int(float(string) * 1024)
|
|
return int(float(string) * 1000)
|
|
if 'm' in string: # matches "mb" or "m"
|
|
string = string.replace('mb', '').replace('m', '').strip()
|
|
if binary:
|
|
return int(float(string) * 1024 ** 2)
|
|
return int(float(string) * 1000 ** 2)
|
|
if 'g' in string: # matches "gb" or "g"
|
|
string = string.replace('gb', '').replace('g', '').strip()
|
|
if binary:
|
|
return int(float(string) * 1024 ** 3)
|
|
return int(float(string) * 1000 ** 3)
|
|
if 't' in string: # matches "tb" or "t"
|
|
string = string.replace('tb', '').replace('t', '').strip()
|
|
if binary:
|
|
return int(float(string) * 1024 ** 4)
|
|
return int(float(string) * 1000 ** 4)
|
|
if 'p' in string: # matches "pb" or "p"
|
|
string = string.replace('pb', '').replace('p', '').strip()
|
|
if binary:
|
|
return int(float(string) * 1024 ** 5)
|
|
return int(float(string) * 1000 ** 5)
|
|
if 'b' in string:
|
|
return int(float(string.replace('b', '').strip()))
|
|
return 0
|
|
except:
|
|
return 0
|
|
|
|
|
|
def number2human(n):
|
|
"""
|
|
>>> number2human(123456.8)
|
|
'123K'
|
|
>>> number2human(123456789.0)
|
|
'123M'
|
|
>>> number2human(9223372036854775808)
|
|
'9.2E'
|
|
"""
|
|
# according to the SI Symbols at
|
|
# https://en.wikipedia.org/w/index.php?title=Names_of_large_numbers§ion=5#Extensions_of_the_standard_dictionary_numbers
|
|
millnames = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']
|
|
try:
|
|
n = float(n)
|
|
except:
|
|
return n
|
|
millidx = max(0, min(len(millnames) - 1,
|
|
int(math.floor(0 if n == 0 else math.log10(abs(n)) / 3))))
|
|
return '{:.1f}{}'.format(n / 10 ** (3 * millidx), millnames[millidx])
|
|
|
|
|
|
def seconds2human(seconds, keep_short=True, full_name=False):
|
|
"""Returns a human readable time range string for a number of seconds.
|
|
|
|
>>> seconds2human(0.125)
|
|
'0.12s'
|
|
>>> seconds2human(1)
|
|
'1s'
|
|
>>> seconds2human(59)
|
|
'59s'
|
|
>>> seconds2human(60)
|
|
'1m'
|
|
>>> seconds2human(61)
|
|
'1m 1s'
|
|
>>> seconds2human(1387775)
|
|
'2W 2D'
|
|
>>> seconds2human('1387775')
|
|
'2W 2D'
|
|
>>> seconds2human('1387775', full_name=True)
|
|
'2weeks 2days'
|
|
>>> seconds2human(1387775, keep_short=False, full_name=True)
|
|
'2weeks 2days 1hour 29minutes 35seconds'
|
|
"""
|
|
seconds = float(seconds)
|
|
|
|
if full_name:
|
|
symbols = (
|
|
('years', 60 * 60 * 24 * 365),
|
|
('months', 60 * 60 * 24 * 30),
|
|
('weeks', 60 * 60 * 24 * 7),
|
|
('days', 60 * 60 * 24),
|
|
('hours', 60 * 60),
|
|
('minutes', 60),
|
|
('seconds', 1),
|
|
('millisecs', 1e-3),
|
|
('microsecs', 1e-6),
|
|
('nanosecs', 1e-9),
|
|
('picosecs', 1e-12),
|
|
)
|
|
else:
|
|
symbols = (
|
|
('Y', 60 * 60 * 24 * 365),
|
|
('M', 60 * 60 * 24 * 30),
|
|
('W', 60 * 60 * 24 * 7),
|
|
('D', 60 * 60 * 24),
|
|
('h', 60 * 60),
|
|
('m', 60),
|
|
('s', 1),
|
|
('ms', 1e-3),
|
|
('us', 1e-6),
|
|
('ns', 1e-9),
|
|
('ps', 1e-12),
|
|
)
|
|
|
|
result = []
|
|
for name, count in symbols:
|
|
value = seconds // count
|
|
if value:
|
|
seconds -= value * count
|
|
if full_name and value == 1:
|
|
name = name.rstrip('s') # "days" becomes "day"
|
|
result.append('{:.0f}{}'.format(value, name))
|
|
|
|
if len(result) > 2 and keep_short:
|
|
return ' '.join(result[:2])
|
|
return ' '.join(result)
|