Ganeti prouds itself of its really good retro-compatibility and API stability. Some of our users haven't upgraded their hardware in the last 2000 years (one century more, one century less) and their Xen-PVM (Parchment and VelluM) does not support Arabic numerals yet.
The already existing support for printing data in Roman numerals had fallen into disrepair lately. This patch responds to the request of our most ancient users, quickly delivered to us via the "cursus publicus", asking for this fundamental functionality to be restored to its old splendour. Signed-off-by: Michele Tartara <[email protected]> --- lib/cli.py | 22 ++++++++++++++-------- lib/client/gnt_cluster.py | 16 ++++++++++------ lib/client/gnt_instance.py | 22 +++++++++++++--------- lib/compat.py | 28 ++++++++++++++++++++++++++++ lib/utils/text.py | 11 +++++++---- 5 files changed, 72 insertions(+), 27 deletions(-) diff --git a/lib/cli.py b/lib/cli.py index 2c20f27..0a4c153 100644 --- a/lib/cli.py +++ b/lib/cli.py @@ -3834,7 +3834,7 @@ class JobExecutor(object): return [row[1:3] for row in self.jobs] -def FormatParamsDictInfo(param_dict, actual): +def FormatParamsDictInfo(param_dict, actual, roman=False): """Formats a parameter dictionary. @type param_dict: dict @@ -3849,9 +3849,10 @@ def FormatParamsDictInfo(param_dict, actual): ret = {} for (key, data) in actual.items(): if isinstance(data, dict) and data: - ret[key] = FormatParamsDictInfo(param_dict.get(key, {}), data) + ret[key] = FormatParamsDictInfo(param_dict.get(key, {}), data, roman) else: - ret[key] = str(param_dict.get(key, "default (%s)" % data)) + default_str = "default (%s)" % compat.TryToRoman(data, roman) + ret[key] = str(compat.TryToRoman(param_dict.get(key, default_str), roman)) return ret @@ -3863,7 +3864,7 @@ def _FormatListInfoDefault(data, def_data): return ret -def FormatPolicyInfo(custom_ipolicy, eff_ipolicy, iscluster): +def FormatPolicyInfo(custom_ipolicy, eff_ipolicy, iscluster, roman=False): """Formats an instance policy. @type custom_ipolicy: dict @@ -3873,6 +3874,8 @@ def FormatPolicyInfo(custom_ipolicy, eff_ipolicy, iscluster): cluster @type iscluster: bool @param iscluster: the policy is at cluster level + @type roman: bool + @param roman: whether to print the values in roman numerals @rtype: list of pairs @return: formatted data, suitable for L{PrintGenericInfo} @@ -3886,14 +3889,14 @@ def FormatPolicyInfo(custom_ipolicy, eff_ipolicy, iscluster): for (k, minmax) in enumerate(custom_minmax): minmax_out.append([ ("%s/%s" % (key, k), - FormatParamsDictInfo(minmax[key], minmax[key])) + FormatParamsDictInfo(minmax[key], minmax[key], roman)) for key in constants.ISPECS_MINMAX_KEYS ]) else: for (k, minmax) in enumerate(eff_ipolicy[constants.ISPECS_MINMAX]): minmax_out.append([ ("%s/%s" % (key, k), - FormatParamsDictInfo({}, minmax[key])) + FormatParamsDictInfo({}, minmax[key], roman)) for key in constants.ISPECS_MINMAX_KEYS ]) ret = [("bounds specs", minmax_out)] @@ -3902,7 +3905,7 @@ def FormatPolicyInfo(custom_ipolicy, eff_ipolicy, iscluster): stdspecs = custom_ipolicy[constants.ISPECS_STD] ret.append( (constants.ISPECS_STD, - FormatParamsDictInfo(stdspecs, stdspecs)) + FormatParamsDictInfo(stdspecs, stdspecs, roman)) ) ret.append( @@ -3910,8 +3913,11 @@ def FormatPolicyInfo(custom_ipolicy, eff_ipolicy, iscluster): _FormatListInfoDefault(custom_ipolicy.get(constants.IPOLICY_DTS), eff_ipolicy[constants.IPOLICY_DTS])) ) + to_roman = compat.TryToRoman ret.extend([ - (key, str(custom_ipolicy.get(key, "default (%s)" % eff_ipolicy[key]))) + (key, str(to_roman(custom_ipolicy.get(key, + "default (%s)" % eff_ipolicy[key]), + roman))) for key in constants.IPOLICY_PARAMETERS ]) return ret diff --git a/lib/client/gnt_cluster.py b/lib/client/gnt_cluster.py index 54ad8c2..6e4fe85 100644 --- a/lib/client/gnt_cluster.py +++ b/lib/client/gnt_cluster.py @@ -502,12 +502,14 @@ def ShowClusterConfig(opts, args): ("Default hypervisor", result["default_hypervisor"]), ("Enabled hypervisors", utils.CommaJoin(enabled_hv)), - ("Hypervisor parameters", _FormatGroupedParams(hvparams)), + ("Hypervisor parameters", _FormatGroupedParams(hvparams, + opts.roman_integers)), ("OS-specific hypervisor parameters", - _FormatGroupedParams(result["os_hvp"])), + _FormatGroupedParams(result["os_hvp"], opts.roman_integers)), - ("OS parameters", _FormatGroupedParams(result["osparams"])), + ("OS parameters", _FormatGroupedParams(result["osparams"], + opts.roman_integers)), ("Hidden OSes", utils.CommaJoin(result["hidden_os"])), ("Blacklisted OSes", utils.CommaJoin(result["blacklisted_os"])), @@ -521,7 +523,8 @@ def ShowClusterConfig(opts, args): convert=opts.roman_integers)), ("mac prefix", result["mac_prefix"]), ("master netdev", result["master_netdev"]), - ("master netmask", result["master_netmask"]), + ("master netmask", compat.TryToRoman(result["master_netmask"], + opts.roman_integers)), ("use external master IP address setup script", result["use_external_mip_script"]), ("lvm volume group", result["volume_group_name"]), @@ -535,7 +538,8 @@ def ShowClusterConfig(opts, args): ("default instance allocator", result["default_iallocator"]), ("default instance allocator parameters", result["default_iallocator_params"]), - ("primary ip version", result["primary_ip_version"]), + ("primary ip version", compat.TryToRoman(result["primary_ip_version"], + opts.roman_integers)), ("preallocation wipe disks", result["prealloc_wipe_disks"]), ("OS search path", utils.CommaJoin(pathutils.OS_SEARCH_PATH)), ("ExtStorage Providers search path", @@ -559,7 +563,7 @@ def ShowClusterConfig(opts, args): _FormatGroupedParams(result["diskparams"], roman=opts.roman_integers)), ("Instance policy - limits for instances", - FormatPolicyInfo(result["ipolicy"], None, True)), + FormatPolicyInfo(result["ipolicy"], None, True, opts.roman_integers)), ] PrintGenericInfo(info) diff --git a/lib/client/gnt_instance.py b/lib/client/gnt_instance.py index fb9dae1..7625170 100644 --- a/lib/client/gnt_instance.py +++ b/lib/client/gnt_instance.py @@ -933,6 +933,7 @@ def _FormatDiskDetails(dev_type, dev, roman): """Formats the logical_id of a disk. """ + if dev_type == constants.DT_DRBD8: drbd_info = dev["drbd_info"] data = [ @@ -944,7 +945,7 @@ def _FormatDiskDetails(dev_type, dev, roman): (drbd_info["secondary_node"], compat.TryToRoman(drbd_info["secondary_minor"], convert=roman))), - ("port", str(compat.TryToRoman(drbd_info["port"], convert=roman))), + ("port", str(compat.TryToRoman(drbd_info["port"], roman))), ("auth key", str(drbd_info["secret"])), ] elif dev_type == constants.DT_PLAIN: @@ -1036,7 +1037,7 @@ def _FormatBlockDevInfo(idx, top_level, dev, roman): else: txt = "child %s" % compat.TryToRoman(idx, convert=roman) if isinstance(dev["size"], int): - nice_size = utils.FormatUnit(dev["size"], "h") + nice_size = utils.FormatUnit(dev["size"], "h", roman) else: nice_size = str(dev["size"]) data = [(txt, "%s, size %s" % (dev["dev_type"], nice_size))] @@ -1071,19 +1072,19 @@ def _FormatBlockDevInfo(idx, top_level, dev, roman): return data -def _FormatInstanceNicInfo(idx, nic): +def _FormatInstanceNicInfo(idx, nic, roman=False): """Helper function for L{_FormatInstanceInfo()}""" (name, uuid, ip, mac, mode, link, vlan, _, netinfo) = nic network_name = None if netinfo: network_name = netinfo["name"] return [ - ("nic/%d" % idx, ""), + ("nic/%s" % str(compat.TryToRoman(idx, roman)), ""), ("MAC", str(mac)), ("IP", str(ip)), ("mode", str(mode)), ("link", str(link)), - ("vlan", str(vlan)), + ("vlan", str(compat.TryToRoman(vlan, roman))), ("network", str(network_name)), ("UUID", str(uuid)), ("name", str(name)), @@ -1150,7 +1151,8 @@ def _FormatInstanceInfo(instance, roman_integers): ("Nodes", _FormatInstanceNodesInfo(instance)), ("Operating system", instance["os"]), ("Operating system parameters", - FormatParamsDictInfo(instance["os_instance"], instance["os_actual"])), + FormatParamsDictInfo(instance["os_instance"], instance["os_actual"], + roman_integers)), ] if "network_port" in instance: @@ -1167,11 +1169,13 @@ def _FormatInstanceInfo(instance, roman_integers): be_actual["memory"] = be_actual[constants.BE_MAXMEM] info.extend([ ("Hypervisor parameters", - FormatParamsDictInfo(instance["hv_instance"], instance["hv_actual"])), + FormatParamsDictInfo(instance["hv_instance"], instance["hv_actual"], + roman_integers)), ("Back-end parameters", - FormatParamsDictInfo(instance["be_instance"], be_actual)), + FormatParamsDictInfo(instance["be_instance"], be_actual, + roman_integers)), ("NICs", [ - _FormatInstanceNicInfo(idx, nic) + _FormatInstanceNicInfo(idx, nic, roman_integers) for (idx, nic) in enumerate(instance["nics"]) ]), ("Disk template", instance["disk_template"]), diff --git a/lib/compat.py b/lib/compat.py index 5f1409e..3c0ba5d 100644 --- a/lib/compat.py +++ b/lib/compat.py @@ -124,6 +124,34 @@ else: partial = functools.partial +def RomanOrRounded(value, rounding, convert=True): + """Try to round the value to the closest integer and return it as a roman + numeral. If the conversion is disabled, or if the roman module could not be + loaded, round the value to the specified level and return it. + + @type value: number + @param value: value to convert + @type rounding: integer + @param rounding: how many decimal digits the number should be rounded to + @type convert: boolean + @param convert: if False, don't try conversion at all + @rtype: string + @return: roman numeral for val, or formatted string representing val if + conversion didn't succeed + + """ + def _FormatOutput(val, r): + format_string = "%0." + str(r) + "f" + return format_string % val + + if roman is not None and convert: + try: + return roman.toRoman(round(value, 0)) + except roman.RomanError: + return _FormatOutput(value, rounding) + return _FormatOutput(value, rounding) + + def TryToRoman(val, convert=True): """Try to convert a value to roman numerals diff --git a/lib/utils/text.py b/lib/utils/text.py index a69af17..4dba573 100644 --- a/lib/utils/text.py +++ b/lib/utils/text.py @@ -29,6 +29,7 @@ import time import collections from ganeti import errors +from ganeti import compat #: Unit checker regexp @@ -131,7 +132,7 @@ def DnsNameGlobPattern(pattern): return r"^%s(\..*)?$" % re.sub(r"\*|\?|[^*?]*", _DnsNameGlobHelper, pattern) -def FormatUnit(value, units): +def FormatUnit(value, units, roman=False): """Formats an incoming number of MiB with the appropriate unit. @type value: int @@ -154,17 +155,19 @@ def FormatUnit(value, units): if units == "m" or (units == "h" and value < 1024): if units == "h": suffix = "M" - return "%d%s" % (round(value, 0), suffix) + return "%s%s" % (compat.RomanOrRounded(value, 0, roman), suffix) elif units == "g" or (units == "h" and value < (1024 * 1024)): if units == "h": suffix = "G" - return "%0.1f%s" % (round(float(value) / 1024, 1), suffix) + return "%s%s" % (compat.RomanOrRounded(float(value) / 1024, 1, roman), + suffix) else: if units == "h": suffix = "T" - return "%0.1f%s" % (round(float(value) / 1024 / 1024, 1), suffix) + return "%s%s" % (compat.RomanOrRounded(float(value) / 1024 / 1024, 1, + roman), suffix) def ParseUnit(input_string): -- 1.9.1.423.g4596e3a
