1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

new filter human_bytes: convert a string (ex: 1Mo, 1K) into bytes (#12074)

* Rework human_readable and human_to_bytes.
New filter human_to_bytes.

* Fix for python 3.
This commit is contained in:
Yannig 2016-08-24 21:04:20 +02:00 committed by Toshio Kuratomi
parent 733f977ad2
commit 27b0f3241b
3 changed files with 112 additions and 67 deletions

View file

@ -31,6 +31,8 @@ BOOLEANS_TRUE = ['y', 'yes', 'on', '1', 'true', 1, True]
BOOLEANS_FALSE = ['n', 'no', 'off', '0', 'false', 0, False] BOOLEANS_FALSE = ['n', 'no', 'off', '0', 'false', 0, False]
BOOLEANS = BOOLEANS_TRUE + BOOLEANS_FALSE BOOLEANS = BOOLEANS_TRUE + BOOLEANS_FALSE
SIZE_RANGES = { 'Y': 1<<80, 'Z': 1<<70, 'E': 1<<60, 'P': 1<<50, 'T': 1<<40, 'G': 1<<30, 'M': 1<<20, 'K': 1<<10, 'B': 1 }
# ansible modules can be written in any language. To simplify # ansible modules can be written in any language. To simplify
# development of Python modules, the functions available here can # development of Python modules, the functions available here can
# be used to do many common tasks # be used to do many common tasks
@ -466,6 +468,72 @@ def heuristic_log_sanitize(data, no_log_values=None):
output = remove_values(output, no_log_values) output = remove_values(output, no_log_values)
return output return output
def bytes_to_human(size, isbits=False, unit=None):
base = 'Bytes'
if isbits:
base = 'bits'
suffix = ''
for suffix, limit in sorted(iteritems(SIZE_RANGES), key=lambda item: -item[1]):
if (unit is None and size >= limit) or unit is not None and unit.upper() == suffix[0]:
break
if limit != 1:
suffix += base[0]
else:
suffix = base
return '%.2f %s' % (float(size)/ limit, suffix)
def human_to_bytes(number, default_unit=None, isbits=False):
'''
Convert number in string format into bytes (ex: '2K' => 2048) or using unit argument
ex:
human_to_bytes('10M') <=> human_to_bytes(10, 'M')
'''
m = re.search('^\s*(\d*\.?\d*)\s*([A-Za-z]+)?', str(number), flags=re.IGNORECASE)
if m is None:
raise ValueError("human_to_bytes() can't interpret following string: %s" % str(number))
try:
num = float(m.group(1))
except:
raise ValueError("human_to_bytes() can't interpret following number: %s (original input string: %s)" % (m.group(1), number))
unit = m.group(2)
if unit is None:
unit = default_unit
if unit is None:
''' No unit given, returning raw number '''
return int(round(num))
range_key = unit[0].upper()
try:
limit = SIZE_RANGES[range_key]
except:
raise ValueError("human_to_bytes() failed to convert %s (unit = %s). The suffix must be one of %s" % (number, unit, ", ".join(SIZE_RANGES.keys())))
# default value
unit_class = 'B'
unit_class_name = 'byte'
# handling bits case
if isbits:
unit_class = 'b'
unit_class_name = 'bit'
# check unit value if more than one character (KB, MB)
if len(unit) > 1:
expect_message = 'expect %s%s or %s' % (range_key, unit_class, range_key)
if range_key == 'B':
expect_message = 'expect %s or %s' % (unit_class, unit_class_name)
if unit_class_name in unit.lower():
pass
elif unit[1] != unit_class:
raise ValueError("human_to_bytes() failed to convert %s. Value is not a valid string (%s)" % (number, expect_message))
return int(round(num * limit))
def is_executable(path): def is_executable(path):
'''is the given path executable? '''is the given path executable?
@ -1468,7 +1536,7 @@ class AnsibleModule(object):
def _check_type_bits(self, value): def _check_type_bits(self, value):
try: try:
self.human_to_bytes(value, bits=True) self.human_to_bytes(value, isbits=True)
except ValueError: except ValueError:
raise TypeError('%s cannot be converted to a Bit value' % type(value)) raise TypeError('%s cannot be converted to a Bit value' % type(value))
@ -2181,53 +2249,13 @@ class AnsibleModule(object):
fh.close() fh.close()
def bytes_to_human(self, size): def bytes_to_human(self, size):
return bytes_to_human(size)
ranges = (
(1 << 70, 'ZB'),
(1 << 60, 'EB'),
(1 << 50, 'PB'),
(1 << 40, 'TB'),
(1 << 30, 'GB'),
(1 << 20, 'MB'),
(1 << 10, 'KB'),
(1, 'Bytes')
)
for limit, suffix in ranges:
if size >= limit:
break
return '%.2f %s' % (float(size)/ limit, suffix)
# for backwards compatibility # for backwards compatibility
pretty_bytes = bytes_to_human pretty_bytes = bytes_to_human
def human_to_bytes(number, bits=False): def human_to_bytes(self, number, isbits=False):
return human_to_bytes(number, isbits)
result = None
suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB']
full = 'Bytes'
if bits:
suffixes = [ x.replace('B', 'b') for x in suffixes ]
full = 'Bits'
if number is None:
result = 0
elif isinstance(number, int):
result = number
elif number.isdigit():
result = int(number)
elif full in number:
result = int(number.replace(full,''))
else:
for i, suffix in enumerate(suffixes):
if suffix in number:
result = int(number.replace(suffix ,'')) * (1024 ** i)
break
if result is None:
raise ValueError("Failed to convert %s. The suffix must be one of %s or %s" % (number, full, ', '.join(suffixes)))
return result
# #
# Backwards compat # Backwards compat

View file

@ -23,6 +23,7 @@ __metaclass__ = type
import math import math
import collections import collections
from ansible import errors from ansible import errors
from ansible.module_utils import basic
def unique(a): def unique(a):
if isinstance(a,collections.Hashable): if isinstance(a,collections.Hashable):
@ -99,30 +100,18 @@ def inversepower(x, base=2):
def human_readable(size, isbits=False, unit=None): def human_readable(size, isbits=False, unit=None):
''' Return a human readable string '''
try:
return basic.bytes_to_human(size, isbits, unit)
except:
raise errors.AnsibleFilterError("human_readable() can't interpret following string: %s" % size)
base = 'bits' if isbits else 'Bytes' def human_to_bytes(size, default_unit=None, isbits=False):
suffix = '' ''' Return bytes count from a human readable string '''
try:
ranges = ( return basic.human_to_bytes(size, default_unit, isbits)
(1<<70, 'Z'), except:
(1<<60, 'E'), raise errors.AnsibleFilterError("human_to_bytes() can't interpret following string: %s" % size)
(1<<50, 'P'),
(1<<40, 'T'),
(1<<30, 'G'),
(1<<20, 'M'),
(1<<10, 'K'),
(1, base)
)
for limit, suffix in ranges:
if (unit is None and size >= limit) or \
unit is not None and unit.upper() == suffix:
break
if limit != 1:
suffix += base[0]
return '%.2f %s' % (float(size)/ limit, suffix)
class FilterModule(object): class FilterModule(object):
''' Ansible math jinja2 filters ''' ''' Ansible math jinja2 filters '''
@ -147,5 +136,6 @@ class FilterModule(object):
# computer theory # computer theory
'human_readable' : human_readable, 'human_readable' : human_readable,
'human_to_bytes' : human_to_bytes,
} }

View file

@ -61,13 +61,40 @@
- 'diff_result.stdout == ""' - 'diff_result.stdout == ""'
- name: Verify human_readable - name: Verify human_readable
tags: "human_readable"
assert: assert:
that: that:
- '"1.00 Bytes" == 1|human_readable'
- '"1.00 bits" == 1|human_readable(isbits=True)'
- '"10.00 KB" == 10240|human_readable' - '"10.00 KB" == 10240|human_readable'
- '"97.66 MB" == 102400000|human_readable' - '"97.66 MB" == 102400000|human_readable'
- '"0.10 GB" == 102400000|human_readable(unit="G")' - '"0.10 GB" == 102400000|human_readable(unit="G")'
- '"0.10 Gb" == 102400000|human_readable(isbits=True, unit="G")' - '"0.10 Gb" == 102400000|human_readable(isbits=True, unit="G")'
- name: Verify human_to_bytes
tags: "human_to_bytes"
assert:
that:
- "{{'0'|human_to_bytes}} == 0"
- "{{'0.1'|human_to_bytes}} == 0"
- "{{'0.9'|human_to_bytes}} == 1"
- "{{'1'|human_to_bytes}} == 1"
- "{{'10.00 KB'|human_to_bytes}} == 10240"
- "{{ '11 MB'|human_to_bytes}} == 11534336"
- "{{ '1.1 GB'|human_to_bytes}} == 1181116006"
- "{{'10.00 Kb'|human_to_bytes(isbits=True)}} == 10240"
- name: Verify human_to_bytes (bad string)
tags: "human_to_bytes"
set_fact: bad_string="{{'10.00 foo'|human_to_bytes}}"
ignore_errors: yes
register: _
- name: Verify human_to_bytes (bad string)
tags: "human_to_bytes"
assert:
that: "{{_.failed}}"
- name: Container lookups with extract - name: Container lookups with extract
assert: assert:
that: that: