
The new function "kimchi.utils.convert_data_size" can be used to convert values from different units, e.g. converting 5 GiB to MiB, 1 GiB to B. Signed-off-by: Crístian Viana <vianac@linux.vnet.ibm.com> --- src/kimchi/i18n.py | 2 + src/kimchi/utils.py | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+) diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py index 2af0be6..df5422f 100644 --- a/src/kimchi/i18n.py +++ b/src/kimchi/i18n.py @@ -267,6 +267,8 @@ messages = { "KCHUTILS0001E": _("Invalid URI %(uri)s"), "KCHUTILS0002E": _("Timeout while running command '%(cmd)s' after %(seconds)s seconds"), "KCHUTILS0003E": _("Unable to choose a virtual machine name"), + "KCHUTILS0004E": _("Invalid data value '%(value)s'"), + "KCHUTILS0005E": _("Invalid data unit '%(unit)s'"), "KCHVMSTOR0002E": _("Invalid storage type. Types supported: 'cdrom', 'disk'"), "KCHVMSTOR0003E": _("The path '%(value)s' is not a valid local/remote path for the device"), diff --git a/src/kimchi/utils.py b/src/kimchi/utils.py index fc5245f..a04d3cf 100644 --- a/src/kimchi/utils.py +++ b/src/kimchi/utils.py @@ -388,3 +388,148 @@ def get_unique_file_name(all_names, name): max_num = max(max_num, int(match.group(re_group_num))) return u'%s (%d)' % (name, max_num + 1) + + +def convert_data_size(value, src_unit, dst_unit=None): + """Convert a data value from one unit to another unit + (e.g. 'MiB' -> 'GiB'). + + The data units supported by this function are made up of one prefix and one + suffix, both optional. The valid prefixes are those defined in the SI + (i.e. metric system) and those defined by the IEC, and the valid suffixes + indicate if the base unit is bit or byte. Take a look at the tables below + for the possible values: + + Prefixes: + + ================================== =================================== + PREFIX (SI) | DESCRIPTION | VALUE PREFIX (IEC) | DESCRIPTION | VALUE + ================================== =================================== + k | kilo | 1000 Ki | kibi | 1024 + ---------------------------------- ----------------------------------- + M | mega | 1000^2 Mi | mebi | 1024^2 + ---------------------------------- ----------------------------------- + G | giga | 1000^3 Gi | gibi | 1024^3 + ---------------------------------- ----------------------------------- + T | tera | 1000^4 Ti | tebi | 1024^4 + ---------------------------------- ----------------------------------- + P | peta | 1000^5 Pi | pebi | 1024^5 + ---------------------------------- ----------------------------------- + E | exa | 1000^6 Ei | exbi | 1024^6 + ---------------------------------- ----------------------------------- + Z | zetta | 1000^7 Zi | zebi | 1024^7 + ---------------------------------- ----------------------------------- + Y | yotta | 1000^8 Yi | yobi | 1024^8 + ================================== =================================== + + Suffixes: + + ==================== + SUFFIX | DESCRIPTION + ==================== + b | bit + -------------------- + B | byte + ==================== + + See http://en.wikipedia.org/wiki/Binary_prefix for more details on + those units. + + If a wrong prefix or suffix is provided, an error will be raised. + + Examples: + convert_data_size(5, 'MiB', 'KiB') -> 5120.0 + convert_data_size(5, 'MiB', 'M') -> 5.24288 + convert_data_size(5, 'MiB', 'GiB') -> 0.0048828125 + convert_data_size(5, 'MiB', 'Tb') -> 4.194304e-05 + convert_data_size(5, 'MiB') -> 5242880.0 + convert_data_size(5, 'mib') -> #ERROR# (invalid src_unit) + + Parameters: + value -- the value to be converted, in the unit specified by 'src_unit'. + this parameter can be of any type which can be cast to float + (e.g. int, float, str). + src_unit -- the unit of 'value', as described above. + if 'src_unit' is empty, the unit 'B' (byte) will be used. + dst_unit -- the unit of the return value. + if 'dst_unit' is empty, the unit 'B' (byte) will be used. + + Return: + A float number representing 'value' ('src_unit') converted to 'dst_unit'. + """ + SI_PREFIXES = ['k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'] + # The IEC prefixes are the equivalent SI prefixes + 'i' + # but, exceptionally, 'k' becomes 'Ki' instead of 'ki'. + IEC_PREFIXES = map(lambda p: 'Ki' if p == 'k' else p + 'i', SI_PREFIXES) + PREFIXES_BY_BASE = {1000: SI_PREFIXES, + 1024: IEC_PREFIXES} + + SUFFIXES_WITH_MULT = {'b': 1, + 'B': 8} + DEFAULT_SUFFIX = 'B' + + DEFAULT_UNIT = 'B' + + # set the default units + if not src_unit: + src_unit = DEFAULT_UNIT + if not dst_unit: + dst_unit = DEFAULT_UNIT + + # set the default suffix + if src_unit[-1] not in SUFFIXES_WITH_MULT: + src_unit += DEFAULT_SUFFIX + if dst_unit[-1] not in SUFFIXES_WITH_MULT: + dst_unit += DEFAULT_SUFFIX + + # split prefix and suffix for better parsing + src_p = src_unit[:-1] + src_s = src_unit[-1] + dst_p = dst_unit[:-1] + dst_s = dst_unit[-1] + + # validate parameters + try: + value = float(value) + except TypeError: + raise InvalidParameter('KCHUTILS0004E', {'value': value}) + if src_p != '' and src_p not in (SI_PREFIXES + IEC_PREFIXES): + raise InvalidParameter('KCHUTILS0005E', {'unit': src_unit}) + if src_s not in SUFFIXES_WITH_MULT: + raise InvalidParameter('KCHUTILS0005E', {'unit': src_unit}) + if dst_p != '' and dst_p not in (SI_PREFIXES + IEC_PREFIXES): + raise InvalidParameter('KCHUTILS0005E', {'unit': dst_unit}) + if dst_s not in SUFFIXES_WITH_MULT: + raise InvalidParameter('KCHUTILS0005E', {'unit': dst_unit}) + + # convert 'value' to the most basic unit (bits)... + bits = value + + for suffix, mult in SUFFIXES_WITH_MULT.iteritems(): + if src_s == suffix: + bits *= mult + break + + if src_p != '': + for base, prefixes in PREFIXES_BY_BASE.iteritems(): + for i, p in enumerate(prefixes): + if src_p == p: + bits *= base**(i + 1) + break + + # ...then convert the value in bits to the destination unit + ret = bits + + for suffix, mult in SUFFIXES_WITH_MULT.iteritems(): + if dst_s == suffix: + ret /= float(mult) + break + + if dst_p != '': + for base, prefixes in PREFIXES_BY_BASE.iteritems(): + for i, p in enumerate(prefixes): + if dst_p == p: + ret /= float(base)**(i + 1) + break + + return ret -- 2.1.0