Commit 9fbfbb7b authored by Iustin Pop's avatar Iustin Pop
Browse files

Enable auto-unit formatting in script output

This patch enables by default the old 'human-readable' option, but in a
slightly different model.

The option is now called "units" and takes either:
 - 'h' for automatic formatting
 - 'm', 'g' or 't' for mebi/gibi/tebibytes

If 'h' is used, we add a unit suffix, otherwise nothing is added so that
parsing is easy.

The default value of this unit is:
  - 'h' if a separator is not passed
  - 'm' if a separator is passed

Reviewed-by: ultrotter
parent 23b8c8d6
......@@ -171,9 +171,9 @@ SEP_OPT = make_option("--separator", default=None,
help="Separator between output fields"
" (defaults to one space)")
USEUNITS_OPT = make_option("--human-readable", default=False,
action="store_true", dest="human_readable",
help="Print sizes in human readable format")
USEUNITS_OPT = make_option("--units", default=None,
dest="units", choices=('h', 'm', 'g', 't'),
help="Specify units for output (one of hmgt)")
FIELDS_OPT = make_option("-o", "--output", dest="output", action="store",
type="string", help="Comma separated list of"
......@@ -747,18 +747,40 @@ def GenericMain(commands, override=None, aliases=None):
def GenerateTable(headers, fields, separator, data,
numfields=None, unitfields=None):
numfields=None, unitfields=None,
units=None):
"""Prints a table with headers and different fields.
Args:
headers: Dict of header titles or None if no headers should be shown
fields: List of fields to show
separator: String used to separate fields or None for spaces
data: Data to be printed
numfields: List of fields to be aligned to right
unitfields: List of fields to be formatted as units
@type headers: dict
@param headers: dictionary mapping field names to headers for
the table
@type fields: list
@param fields: the field names corresponding to each row in
the data field
@param separator: the separator to be used; if this is None,
the default 'smart' algorithm is used which computes optimal
field width, otherwise just the separator is used between
each field
@type data: list
@param data: a list of lists, each sublist being one row to be output
@type numfields: list
@param numfields: a list with the fields that hold numeric
values and thus should be right-aligned
@type unitfields: list
@param unitfields: a list with the fields that hold numeric
values that should be formatted with the units field
@type units: string or None
@param units: the units we should use for formatting, or None for
automatic choice (human-readable for non-separator usage, otherwise
megabytes); this is a one-letter string
"""
if units is None:
if separator:
units = "m"
else:
units = "h"
if numfields is None:
numfields = []
if unitfields is None:
......@@ -795,7 +817,7 @@ def GenerateTable(headers, fields, separator, data,
except ValueError:
pass
else:
val = row[idx] = utils.FormatUnit(val)
val = row[idx] = utils.FormatUnit(val, units)
val = row[idx] = str(val)
if separator is None:
mlens[idx] = max(mlens[idx], len(val))
......
......@@ -638,23 +638,40 @@ def BuildShellCmd(template, *args):
return template % args
def FormatUnit(value):
def FormatUnit(value, units):
"""Formats an incoming number of MiB with the appropriate unit.
@type value: int
@param value: integer representing the value in MiB (1048576)
@type units: char
@param units: the type of formatting we should do:
- 'h' for automatic scaling
- 'm' for MiBs
- 'g' for GiBs
- 't' for TiBs
@rtype: str
@return: the formatted value (with suffix)
"""
if value < 1024:
return "%dM" % round(value, 0)
if units not in ('m', 'g', 't', 'h'):
raise errors.ProgrammerError("Invalid unit specified '%s'" % str(units))
elif value < (1024 * 1024):
return "%0.1fG" % round(float(value) / 1024, 1)
suffix = ''
if units == 'm' or (units == 'h' and value < 1024):
if units == 'h':
suffix = 'M'
return "%d%s" % (round(value, 0), 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)
else:
return "%0.1fT" % round(float(value) / 1024 / 1024, 1)
if units == 'h':
suffix = 'T'
return "%0.1f%s" % (round(float(value) / 1024 / 1024, 1), suffix)
def ParseUnit(input_string):
......
......@@ -228,11 +228,7 @@ def ListInstances(opts, args):
else:
headers = None
if opts.human_readable:
unitfields = ["be/memory", "oper_ram", "sd(a|b)_size", "disk\.size/.*"]
else:
unitfields = None
unitfields = ["be/memory", "oper_ram", "sd(a|b)_size", "disk\.size/.*"]
numfields = ["be/memory", "oper_ram", "sd(a|b)_size", "be/vcpus",
"serial_no", "(disk|nic)\.count", "disk\.size/.*"]
......@@ -270,7 +266,7 @@ def ListInstances(opts, args):
data = GenerateTable(separator=opts.separator, headers=headers,
fields=selected_fields, unitfields=unitfields,
numfields=numfields, data=output)
numfields=numfields, data=output, units=opts.units)
for line in data:
ToStdout(line)
......
......@@ -106,7 +106,7 @@ def ListJobs(opts, args):
data = GenerateTable(separator=opts.separator, headers=headers,
fields=selected_fields, unitfields=unitfields,
numfields=numfields, data=output)
numfields=numfields, data=output, units=opts.units)
for line in data:
ToStdout(line)
......
......@@ -115,10 +115,7 @@ def ListNodes(opts, args):
else:
headers = None
if opts.human_readable:
unitfields = ["dtotal", "dfree", "mtotal", "mnode", "mfree"]
else:
unitfields = None
unitfields = ["dtotal", "dfree", "mtotal", "mnode", "mfree"]
numfields = ["dtotal", "dfree",
"mtotal", "mnode", "mfree",
......@@ -138,7 +135,7 @@ def ListNodes(opts, args):
data = GenerateTable(separator=opts.separator, headers=headers,
fields=selected_fields, unitfields=unitfields,
numfields=numfields, data=output)
numfields=numfields, data=output, units=opts.units)
for line in data:
ToStdout(line)
......@@ -343,16 +340,13 @@ def ListVolumes(opts, args):
else:
headers = None
if opts.human_readable:
unitfields = ["size"]
else:
unitfields = None
unitfields = ["size"]
numfields = ["size"]
data = GenerateTable(separator=opts.separator, headers=headers,
fields=selected_fields, unitfields=unitfields,
numfields=numfields, data=output)
numfields=numfields, data=output, units=opts.units)
for line in data:
ToStdout(line)
......
......@@ -55,7 +55,8 @@ def ListOS(opts, args):
headers = None
data = GenerateTable(separator=None, headers=headers, fields=["name"],
data=[[row[0]] for row in result if row[1]])
data=[[row[0]] for row in result if row[1]],
units=None)
for line in data:
ToStdout(line)
......
......@@ -324,21 +324,42 @@ class TestFormatUnit(unittest.TestCase):
"""Test case for the FormatUnit function"""
def testMiB(self):
self.assertEqual(FormatUnit(1), '1M')
self.assertEqual(FormatUnit(100), '100M')
self.assertEqual(FormatUnit(1023), '1023M')
self.assertEqual(FormatUnit(1, 'h'), '1M')
self.assertEqual(FormatUnit(100, 'h'), '100M')
self.assertEqual(FormatUnit(1023, 'h'), '1023M')
self.assertEqual(FormatUnit(1, 'm'), '1')
self.assertEqual(FormatUnit(100, 'm'), '100')
self.assertEqual(FormatUnit(1023, 'm'), '1023')
self.assertEqual(FormatUnit(1024, 'm'), '1024')
self.assertEqual(FormatUnit(1536, 'm'), '1536')
self.assertEqual(FormatUnit(17133, 'm'), '17133')
self.assertEqual(FormatUnit(1024 * 1024 - 1, 'm'), '1048575')
def testGiB(self):
self.assertEqual(FormatUnit(1024), '1.0G')
self.assertEqual(FormatUnit(1536), '1.5G')
self.assertEqual(FormatUnit(17133), '16.7G')
self.assertEqual(FormatUnit(1024 * 1024 - 1), '1024.0G')
self.assertEqual(FormatUnit(1024, 'h'), '1.0G')
self.assertEqual(FormatUnit(1536, 'h'), '1.5G')
self.assertEqual(FormatUnit(17133, 'h'), '16.7G')
self.assertEqual(FormatUnit(1024 * 1024 - 1, 'h'), '1024.0G')
self.assertEqual(FormatUnit(1024, 'g'), '1.0')
self.assertEqual(FormatUnit(1536, 'g'), '1.5')
self.assertEqual(FormatUnit(17133, 'g'), '16.7')
self.assertEqual(FormatUnit(1024 * 1024 - 1, 'g'), '1024.0')
self.assertEqual(FormatUnit(1024 * 1024, 'g'), '1024.0')
self.assertEqual(FormatUnit(5120 * 1024, 'g'), '5120.0')
self.assertEqual(FormatUnit(29829 * 1024, 'g'), '29829.0')
def testTiB(self):
self.assertEqual(FormatUnit(1024 * 1024), '1.0T')
self.assertEqual(FormatUnit(5120 * 1024), '5.0T')
self.assertEqual(FormatUnit(29829 * 1024), '29.1T')
self.assertEqual(FormatUnit(1024 * 1024, 'h'), '1.0T')
self.assertEqual(FormatUnit(5120 * 1024, 'h'), '5.0T')
self.assertEqual(FormatUnit(29829 * 1024, 'h'), '29.1T')
self.assertEqual(FormatUnit(1024 * 1024, 't'), '1.0')
self.assertEqual(FormatUnit(5120 * 1024, 't'), '5.0')
self.assertEqual(FormatUnit(29829 * 1024, 't'), '29.1')
class TestParseUnit(unittest.TestCase):
"""Test case for the ParseUnit function"""
......
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