From 6940856f233f4d365a119eed90ff88fd918f6916 Mon Sep 17 00:00:00 2001 From: Guillaume Abrioux Date: Tue, 16 Nov 2021 20:32:38 +0100 Subject: [PATCH] ceph-volume: human_readable_size() refactor This commit refactors the `human_readable_size()` function. The current implementation has a couple of issues: in a 'human readable' mindset, I would expect `human_readable_size(1024)` to return '1.00 KB' instead of '1024.00 KB'. ``` In [1]: from ceph_volume.util.disk import human_readable_size In [2]: human_readable_size(1024) Out[2]: '1024.00 B' In [3]: human_readable_size(1024*1024) Out[3]: '1024.00 KB' ``` Also, it doesn't support PB unit: ``` In [4]: human_readable_size(1024*1024*1024*1024*1024) Out[4]: '1024.00 TB' In [5]: human_readable_size(1024*1024*1024*1024*1024*1024) --------------------------------------------------------------------------- IndexError Traceback (most recent call last) in ----> 1 human_readable_size(1024*1024*1024*1024*1024*1024) ~/GIT/ceph/src/ceph-volume/ceph_volume/util/disk.py in human_readable_size(size) 640 return "{size:.2f} {suffix}".format( 641 size=size, --> 642 suffix=suffixes[suffix_index]) 643 644 IndexError: list index out of range ``` This commit fixes this. Fixes: https://tracker.ceph.com/issues/48492 Signed-off-by: Guillaume Abrioux --- .../ceph_volume/tests/util/test_disk.py | 15 +++++++++-- src/ceph-volume/ceph_volume/util/disk.py | 25 ++++++++++++------- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/ceph-volume/ceph_volume/tests/util/test_disk.py b/src/ceph-volume/ceph_volume/tests/util/test_disk.py index 5f4d57343e4c2..44f19e036fa47 100644 --- a/src/ceph-volume/ceph_volume/tests/util/test_disk.py +++ b/src/ceph-volume/ceph_volume/tests/util/test_disk.py @@ -124,6 +124,9 @@ def test_terabytes(self): result = disk.human_readable_size(81.2*1024*1024*1024*1024) assert result == '81.20 TB' + def test_petabytes(self): + result = disk.human_readable_size(9.23*1024*1024*1024*1024*1024) + assert result == '9.23 PB' class TestSizeFromHumanReadable(object): @@ -143,10 +146,14 @@ def test_gigabytes(self): result = disk.size_from_human_readable('2 G') assert result == disk.Size(gb=2) - def test_terrabytes(self): + def test_terabytes(self): result = disk.size_from_human_readable('2 T') assert result == disk.Size(tb=2) + def test_petabytes(self): + result = disk.size_from_human_readable('2 P') + assert result == disk.Size(pb=2) + def test_case(self): result = disk.size_from_human_readable('2 t') assert result == disk.Size(tb=2) @@ -182,10 +189,14 @@ def test_gigabytes(self): result = disk.Size.parse('2G') assert result == disk.Size(gb=2) - def test_terrabytes(self): + def test_terabytes(self): result = disk.Size.parse('2T') assert result == disk.Size(tb=2) + def test_petabytes(self): + result = disk.Size.parse('2P') + assert result == disk.Size(pb=2) + def test_tb(self): result = disk.Size.parse('2Tb') assert result == disk.Size(tb=2) diff --git a/src/ceph-volume/ceph_volume/util/disk.py b/src/ceph-volume/ceph_volume/util/disk.py index 3d9e19c3efe4a..88db0513817a9 100644 --- a/src/ceph-volume/ceph_volume/util/disk.py +++ b/src/ceph-volume/ceph_volume/util/disk.py @@ -402,6 +402,8 @@ class FloatKB(BaseFloatUnit): class FloatTB(BaseFloatUnit): pass +class FloatPB(BaseFloatUnit): + pass class Size(object): """ @@ -456,10 +458,10 @@ class Size(object): @classmethod def parse(cls, size): if (len(size) > 2 and - size[-2].lower() in ['k', 'm', 'g', 't'] and + size[-2].lower() in ['k', 'm', 'g', 't', 'p'] and size[-1].lower() == 'b'): return cls(**{size[-2:].lower(): float(size[0:-2])}) - elif size[-1].lower() in ['b', 'k', 'm', 'g', 't']: + elif size[-1].lower() in ['b', 'k', 'm', 'g', 't', 'p']: return cls(**{size[-1].lower(): float(size[0:-1])}) else: return cls(b=float(size)) @@ -474,6 +476,7 @@ def __init__(self, multiplier=1024, **kw): [('m', 'mb', 'megabytes'), self._multiplier ** 2], [('g', 'gb', 'gigabytes'), self._multiplier ** 3], [('t', 'tb', 'terabytes'), self._multiplier ** 4], + [('p', 'pb', 'petabytes'), self._multiplier ** 5] ] # and mappings for units-to-formatters, including bytes and aliases for # each @@ -483,6 +486,7 @@ def __init__(self, multiplier=1024, **kw): [('mb', 'megabytes'), FloatMB], [('gb', 'gigabytes'), FloatGB], [('tb', 'terabytes'), FloatTB], + [('pb', 'petabytes'), FloatPB], ] self._formatters = {} for key, value in format_aliases: @@ -516,7 +520,7 @@ def _get_best_format(self): than 1024. This allows to represent size in the most readable format available """ - for unit in ['b', 'kb', 'mb', 'gb', 'tb']: + for unit in ['b', 'kb', 'mb', 'gb', 'tb', 'pb']: if getattr(self, unit) > 1024: continue return getattr(self, unit) @@ -632,14 +636,15 @@ def human_readable_size(size): Take a size in bytes, and transform it into a human readable size with up to two decimals of precision. """ - suffixes = ['B', 'KB', 'MB', 'GB', 'TB'] - suffix_index = 0 - while size > 1024: - suffix_index += 1 - size = size / 1024.0 + suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] + for suffix in suffixes: + if size >= 1024: + size = size / 1024 + else: + break return "{size:.2f} {suffix}".format( size=size, - suffix=suffixes[suffix_index]) + suffix=suffix) def size_from_human_readable(s): @@ -651,6 +656,8 @@ def size_from_human_readable(s): if s[-1].isdigit(): return Size(b=float(s)) n = float(s[:-1]) + if s[-1].lower() == 'p': + return Size(pb=n) if s[-1].lower() == 't': return Size(tb=n) if s[-1].lower() == 'g':