Commit 47d2185e authored by Michele Tartara's avatar Michele Tartara
Browse files

Upgrade Roman numerals support

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: default avatarMichele Tartara <>
Reviewed-by: default avatarKlaus Aehlig <>
parent f6004843
......@@ -3834,7 +3834,7 @@ class JobExecutor(object):
return [row[1:3] for row in]
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)
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):
@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):
("%s/%s" % (key, k),
FormatParamsDictInfo(minmax[key], minmax[key]))
FormatParamsDictInfo(minmax[key], minmax[key], roman))
for key in constants.ISPECS_MINMAX_KEYS
for (k, minmax) in enumerate(eff_ipolicy[constants.ISPECS_MINMAX]):
("%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]
FormatParamsDictInfo(stdspecs, stdspecs))
FormatParamsDictInfo(stdspecs, stdspecs, roman))
......@@ -3910,8 +3913,11 @@ def FormatPolicyInfo(custom_ipolicy, eff_ipolicy, iscluster):
to_roman = compat.TryToRoman
(key, str(custom_ipolicy.get(key, "default (%s)" % eff_ipolicy[key])))
(key, str(to_roman(custom_ipolicy.get(key,
"default (%s)" % eff_ipolicy[key]),
for key in constants.IPOLICY_PARAMETERS
return ret
......@@ -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,
("OS-specific hypervisor parameters",
_FormatGroupedParams(result["os_hvp"], opts.roman_integers)),
("OS parameters", _FormatGroupedParams(result["osparams"])),
("OS parameters", _FormatGroupedParams(result["osparams"],
("Hidden OSes", utils.CommaJoin(result["hidden_os"])),
("Blacklisted OSes", utils.CommaJoin(result["blacklisted_os"])),
......@@ -521,7 +523,8 @@ def ShowClusterConfig(opts, args):
("mac prefix", result["mac_prefix"]),
("master netdev", result["master_netdev"]),
("master netmask", result["master_netmask"]),
("master netmask", compat.TryToRoman(result["master_netmask"],
("use external master IP address setup 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",
("primary ip version", result["primary_ip_version"]),
("primary ip version", compat.TryToRoman(result["primary_ip_version"],
("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)),
......@@ -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):
("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):
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)
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"],
if "network_port" in instance:
......@@ -1167,11 +1169,13 @@ def _FormatInstanceInfo(instance, roman_integers):
be_actual["memory"] = be_actual[constants.BE_MAXMEM]
("Hypervisor parameters",
FormatParamsDictInfo(instance["hv_instance"], instance["hv_actual"])),
FormatParamsDictInfo(instance["hv_instance"], instance["hv_actual"],
("Back-end parameters",
FormatParamsDictInfo(instance["be_instance"], be_actual)),
FormatParamsDictInfo(instance["be_instance"], be_actual,
("NICs", [
_FormatInstanceNicInfo(idx, nic)
_FormatInstanceNicInfo(idx, nic, roman_integers)
for (idx, nic) in enumerate(instance["nics"])
("Disk template", instance["disk_template"]),
......@@ -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:
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
......@@ -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),
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):
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment